diff --git a/bin/subiquity-tui b/bin/subiquity-tui index f41c8594..2e179817 100755 --- a/bin/subiquity-tui +++ b/bin/subiquity-tui @@ -19,6 +19,7 @@ import sys import logging import signal +from subiquitycore.i18n import * from subiquitycore.log import setup_logger from subiquitycore import __version__ as VERSION from subiquitycore.core import ApplicationError diff --git a/debian/control b/debian/control index 9f4895bd..9bfecf32 100644 --- a/debian/control +++ b/debian/control @@ -6,9 +6,11 @@ Build-Depends: bzr, debhelper (>= 9), dh-python, dh-systemd, + iso-codes, git, python3, python3-setuptools, + python3-distutils-extra, python3-yaml, python3-attr Standards-Version: 3.9.5 @@ -23,6 +25,7 @@ Depends: curtin, python3, python3-attr, subiquitycore, + iso-codes, ${misc:Depends}, ${python3:Depends} Description: Ubuntu Server Installer diff --git a/debian/subiquity-tools.install b/debian/subiquity-tools.install deleted file mode 100644 index 646062c4..00000000 --- a/debian/subiquity-tools.install +++ /dev/null @@ -1 +0,0 @@ -installer usr/share/subiquity/ diff --git a/debian/subiquitycore.install b/debian/subiquitycore.install index 27074d60..3bd5afb9 100644 --- a/debian/subiquitycore.install +++ b/debian/subiquitycore.install @@ -1 +1,2 @@ usr/share/subiquity/subiquitycore +usr/share/locale diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 00000000..e68d5211 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,19 @@ +[encoding: UTF-8] +subiquity/controllers/filesystem.py +subiquity/controllers/identity.py +subiquity/controllers/installpath.py +subiquity/controllers/installprogress.py +subiquity/controllers/welcome.py +subiquitycore/controllers/network.py +subiquitycore/ui/buttons.py +subiquitycore/ui/interactive.py +subiquitycore/ui/views/network_configure_interface.py +subiquitycore/ui/views/network_configure_manual_interface.py +subiquitycore/ui/views/network.py +subiquity/models/installpath.py +subiquity/ui/mount.py +subiquity/ui/views/filesystem/filesystem.py +subiquity/ui/views/filesystem/guided.py +subiquity/ui/views/identity.py +subiquity/ui/views/installprogress.py +subiquity/ui/views/welcome.py diff --git a/po/en_US.po b/po/en_US.po new file mode 100644 index 00000000..46993fab --- /dev/null +++ b/po/en_US.po @@ -0,0 +1,558 @@ +# English translation for subiquity +# Copyright (C) 2017 Canonical Ltd, and Rosetta Contributors 2017 +# This file is distributed under the same license as the subiquity package. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: subiquity\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-09-15 18:29+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../subiquity/controllers/filesystem.py:67 +#: ../subiquity/controllers/filesystem.py:74 +#: ../subiquity/controllers/filesystem.py:81 +msgid "Filesystem setup" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:68 +msgid "Choose guided or manual partitioning" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:75 +msgid "Select available disks to format and mount" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:82 +msgid "Choose the installation target" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:96 +msgid "Filesystem error" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:97 +msgid "Error while installing Ubuntu" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:98 +msgid "Failed to obtain write permissions to /tmp" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:140 +msgid "Partition, format, and mount {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:141 +msgid "Partition the disk, or format the entire device without partitions" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:151 +msgid "Select whole disk, or partition, to format and mount." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:158 +msgid "Edit partition details format and mount." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:265 +msgid "Create Logical Volume Group (\"LVM2\") disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:266 +#: ../subiquity/controllers/filesystem.py:276 +#: ../subiquity/controllers/filesystem.py:289 +msgid "ENTER on a disk will show detailed information for that disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:268 +msgid "" +"Use SPACE to select disks to form your LVM2 volume group, and then specify " +"the Volume Group name. " +msgstr "" + +#: ../subiquity/controllers/filesystem.py:275 +msgid "Create software RAID (\"MD\") disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:278 +msgid "" +"Use SPACE to select disks to form your RAID array, and then specify the RAID " +"parameters. Multiple-disk arrays work best when all the disks in an array " +"are the same size and speed." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:288 +msgid "Create hierarchical storage (\"bcache\") disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:291 +msgid "" +"Use SPACE to select a cache disk and a backing disk to form your bcache " +"device." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:306 +msgid "Format and/or mount {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:307 +msgid "Format or mount whole disk." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:316 +msgid "Mount partition {} of {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:317 +msgid "Mount partition." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:319 +msgid "Format and mount partition {} of {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:320 +msgid "Format and mount partition." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:385 +msgid "Select next or previous disks with n and p" +msgstr "" + +#: ../subiquity/controllers/identity.py:36 +msgid "Profile setup" +msgstr "" + +#: ../subiquity/controllers/identity.py:37 +msgid "" +"Enter the username and password (or ssh identity) you will use to log in to " +"the system." +msgstr "" + +#: ../subiquity/controllers/installpath.py:45 +msgid "" +"Welcome to Ubuntu! The world's favorite platform for clouds, clusters, and " +"amazing internet things. This is the installer for Ubuntu on servers and " +"internet devices." +msgstr "" + +#: ../subiquity/controllers/installpath.py:49 +msgid "Use UP, DOWN arrow keys, and ENTER, to navigate options" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:74 +msgid "An error occurred during installation" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:75 +msgid "Please report this error in Launchpad" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:76 +msgid "An error has occurred." +msgstr "" + +#: ../subiquity/controllers/installprogress.py:137 +#: ../subiquity/controllers/installprogress.py:228 +msgid "Running postinstall step" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:165 +msgid "Installation complete!" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:167 +msgid "Finished install!" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:215 +msgid "Installing system" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:216 +msgid "Please wait for the installation to finish." +msgstr "" + +#: ../subiquity/controllers/installprogress.py:217 +msgid "Thank you for using Ubuntu!" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:226 +msgid "Running install step" +msgstr "" + +#: ../subiquity/controllers/welcome.py:31 +msgid "Please choose your preferred language" +msgstr "" + +#: ../subiquity/controllers/welcome.py:32 +msgid "Use UP, DOWN and ENTER keys to select your language." +msgstr "" + +#: ../subiquitycore/controllers/network.py:347 +msgid "Network connections" +msgstr "" + +#: ../subiquitycore/controllers/network.py:348 +msgid "" +"Configure at least one interface this server can use to talk to other " +"machines, and which preferably provides sufficient access for updates." +msgstr "" + +#: ../subiquitycore/controllers/network.py:351 +msgid "Select an interface to configure it or select Done to continue" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:37 +msgid "Start" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:38 +msgid "Save" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:39 +msgid "Finish" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:40 +msgid "OK" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:41 +msgid "Confirm" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:42 +msgid "Done" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:43 +#: ../subiquity/ui/views/filesystem/filesystem.py:60 +msgid "Continue" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:45 +msgid "Reset" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:47 +msgid "Cancel" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:48 +msgid "Back" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:114 +msgid "Yes" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:114 +#: ../subiquity/ui/views/filesystem/filesystem.py:59 +msgid "No" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:121 +msgid "Close" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:122 +msgid "Help" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:66 +msgid "Use a static IPv4 configuration" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:69 +msgid "Use DHCPv4 on this interface" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:72 +#: ../subiquitycore/ui/views/network_configure_interface.py:91 +msgid "Do not use" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:85 +msgid "Use a static IPv6 configuration" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:88 +msgid "Use DHCPv6 on this interface" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:102 +msgid "Configure WIFI settings" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:67 +msgid "Subnet:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:68 +msgid "Address:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:69 +msgid "Gateway:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:70 +msgid "Name servers:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:70 +msgid "IP addresses, comma separated" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:71 +msgid "Search domains:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:71 +msgid "Domains, comma separated" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:122 +#, python-format +msgid "CIDR e.g. %s" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:158 +msgid "Set this as default gateway" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:161 +msgid "This will be your default gateway" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:51 +msgid "Applying network config" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:65 +#, python-format +msgid "Associated to '%s', will associate to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:67 +#, python-format +msgid "Associated to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:69 +#, python-format +msgid "No access point configured, but associated to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:72 +#, python-format +msgid "Will associate to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:74 +msgid "No access point configured" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:94 +#, python-format +msgid "Will use DHCP for IPv%s, currently has address%%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:95 +#, python-format +msgid "Will use DHCP for IPv%s" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:98 +#, python-format +msgid "Using static address%%s for IPv%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:99 +#, python-format +msgid "Will use static address%%s for IPv%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:101 +#, python-format +msgid "Currently has address%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:104 +#, python-format +msgid "Has no IPv%s configuration, currently has address%%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:106 +#, python-format +msgid "IPv%s is not configured" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:165 +msgid "Not connected" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:204 +#, python-format +msgid " IPv4 default route %s." +msgstr "" + +#: ../subiquity/models/installpath.py:31 +msgid "Install Ubuntu" +msgstr "" + +#: ../subiquity/ui/mount.py:53 +msgid "other" +msgstr "" + +#: ../subiquity/ui/mount.py:55 +msgid "leave unmounted" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:47 +msgid "" +"\n" +"Selecting Continue below will result of the loss of data on the disks " +"selected to be formatted.\n" +"\n" +"Are you sure you want to continue?\n" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:63 +msgid "Confirm destructive action" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:80 +msgid "FILE SYSTEM SUMMARY" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:84 +msgid "AVAILABLE DEVICES" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:208 +msgid "ADD/EDIT PARTITIONS" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:211 +msgid "ADD FIRST PARTITION" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:214 +msgid "EDIT PARTITIONS" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:222 +msgid "No disks available." +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:33 +msgid "" +"The installer can guide you through partitioning a disk or, if you prefer, " +"you can do it manually. If you choose guided partitioning you will still " +"have a chance to review and modify the results." +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:42 +msgid "Guided" +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:43 +msgid "Manual" +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:78 +msgid "Choose the disk to install to:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:49 +msgid "Your name:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:51 +msgid "Your server's name:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:52 +msgid "The name it uses when it talks to other computers." +msgstr "" + +#: ../subiquity/ui/views/identity.py:53 +msgid "Pick a username:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:54 +msgid "Choose a password:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:55 +msgid "Confirm your password:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:57 +msgid "Import SSH identity:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:58 +msgid "" +"Input your SSH user id from Ubuntu SSO (sso:email), Launchpad (lp:username) " +"or Github (gh:username)." +msgstr "" + +#: ../subiquity/ui/views/identity.py:63 +msgid "Real name must not be empty." +msgstr "" + +#: ../subiquity/ui/views/identity.py:65 +msgid "Realname too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/identity.py:69 +msgid "Server name must not be empty" +msgstr "" + +#: ../subiquity/ui/views/identity.py:72 +msgid "Server name too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/identity.py:76 +msgid "Username missing" +msgstr "" + +#: ../subiquity/ui/views/identity.py:79 +msgid "Username too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/identity.py:85 +msgid "Password must be set" +msgstr "" + +#: ../subiquity/ui/views/identity.py:89 +msgid "Passwords do not match" +msgstr "" + +#: ../subiquity/ui/views/identity.py:94 +msgid "SSH id too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/installprogress.py:65 +msgid "Reboot Now" +msgstr "" + +#: ../subiquity/ui/views/installprogress.py:68 +msgid "Quit Installer" +msgstr "" + +#: ../subiquity/ui/views/welcome.py:40 +msgid "(More language choices will appear in time)" +msgstr "" diff --git a/po/ru.po b/po/ru.po new file mode 100644 index 00000000..6c6a9c51 --- /dev/null +++ b/po/ru.po @@ -0,0 +1,558 @@ +# Russian translation for subiquity +# Copyright (C) 2017 Canonical Ltd, and Rosetta Contributors 2017 +# This file is distributed under the same license as the subiquity package. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: subiquity\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-09-15 18:41+0100\n" +"PO-Revision-Date: 2017-09-04 14:44+0100\n" +"Last-Translator: Dimitri Ledkov \n" +"Language-Team: Russian \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../subiquity/controllers/filesystem.py:67 +#: ../subiquity/controllers/filesystem.py:74 +#: ../subiquity/controllers/filesystem.py:81 +msgid "Filesystem setup" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:68 +msgid "Choose guided or manual partitioning" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:75 +msgid "Select available disks to format and mount" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:82 +msgid "Choose the installation target" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:96 +msgid "Filesystem error" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:97 +msgid "Error while installing Ubuntu" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:98 +msgid "Failed to obtain write permissions to /tmp" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:140 +msgid "Partition, format, and mount {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:141 +msgid "Partition the disk, or format the entire device without partitions" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:151 +msgid "Select whole disk, or partition, to format and mount." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:158 +msgid "Edit partition details format and mount." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:265 +msgid "Create Logical Volume Group (\"LVM2\") disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:266 +#: ../subiquity/controllers/filesystem.py:276 +#: ../subiquity/controllers/filesystem.py:289 +msgid "ENTER on a disk will show detailed information for that disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:268 +msgid "" +"Use SPACE to select disks to form your LVM2 volume group, and then specify " +"the Volume Group name. " +msgstr "" + +#: ../subiquity/controllers/filesystem.py:275 +msgid "Create software RAID (\"MD\") disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:278 +msgid "" +"Use SPACE to select disks to form your RAID array, and then specify the RAID " +"parameters. Multiple-disk arrays work best when all the disks in an array " +"are the same size and speed." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:288 +msgid "Create hierarchical storage (\"bcache\") disk" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:291 +msgid "" +"Use SPACE to select a cache disk and a backing disk to form your bcache " +"device." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:306 +msgid "Format and/or mount {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:307 +msgid "Format or mount whole disk." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:316 +msgid "Mount partition {} of {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:317 +msgid "Mount partition." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:319 +msgid "Format and mount partition {} of {}" +msgstr "" + +#: ../subiquity/controllers/filesystem.py:320 +msgid "Format and mount partition." +msgstr "" + +#: ../subiquity/controllers/filesystem.py:385 +msgid "Select next or previous disks with n and p" +msgstr "" + +#: ../subiquity/controllers/identity.py:36 +msgid "Profile setup" +msgstr "" + +#: ../subiquity/controllers/identity.py:37 +msgid "" +"Enter the username and password (or ssh identity) you will use to log in to " +"the system." +msgstr "" + +#: ../subiquity/controllers/installpath.py:45 +msgid "" +"Welcome to Ubuntu! The world's favorite platform for clouds, clusters, and " +"amazing internet things. This is the installer for Ubuntu on servers and " +"internet devices." +msgstr "" + +#: ../subiquity/controllers/installpath.py:49 +msgid "Use UP, DOWN arrow keys, and ENTER, to navigate options" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:74 +msgid "An error occurred during installation" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:75 +msgid "Please report this error in Launchpad" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:76 +msgid "An error has occurred." +msgstr "" + +#: ../subiquity/controllers/installprogress.py:137 +#: ../subiquity/controllers/installprogress.py:228 +msgid "Running postinstall step" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:165 +msgid "Installation complete!" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:167 +msgid "Finished install!" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:215 +msgid "Installing system" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:216 +msgid "Please wait for the installation to finish." +msgstr "" + +#: ../subiquity/controllers/installprogress.py:217 +msgid "Thank you for using Ubuntu!" +msgstr "" + +#: ../subiquity/controllers/installprogress.py:226 +msgid "Running install step" +msgstr "" + +#: ../subiquity/controllers/welcome.py:31 +msgid "Please choose your preferred language" +msgstr "Выберите предпочитаемый язык" + +#: ../subiquity/controllers/welcome.py:32 +msgid "Use UP, DOWN and ENTER keys to select your language." +msgstr "" + +#: ../subiquitycore/controllers/network.py:347 +msgid "Network connections" +msgstr "" + +#: ../subiquitycore/controllers/network.py:348 +msgid "" +"Configure at least one interface this server can use to talk to other " +"machines, and which preferably provides sufficient access for updates." +msgstr "" + +#: ../subiquitycore/controllers/network.py:351 +msgid "Select an interface to configure it or select Done to continue" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:37 +msgid "Start" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:38 +msgid "Save" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:39 +msgid "Finish" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:40 +msgid "OK" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:41 +msgid "Confirm" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:42 +msgid "Done" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:43 +#: ../subiquity/ui/views/filesystem/filesystem.py:60 +msgid "Continue" +msgstr "Продолжить" + +#: ../subiquitycore/ui/buttons.py:45 +msgid "Reset" +msgstr "" + +#: ../subiquitycore/ui/buttons.py:47 +msgid "Cancel" +msgstr "Отменить" + +#: ../subiquitycore/ui/buttons.py:48 +msgid "Back" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:114 +msgid "Yes" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:114 +#: ../subiquity/ui/views/filesystem/filesystem.py:59 +msgid "No" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:121 +msgid "Close" +msgstr "" + +#: ../subiquitycore/ui/interactive.py:122 +msgid "Help" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:66 +msgid "Use a static IPv4 configuration" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:69 +msgid "Use DHCPv4 on this interface" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:72 +#: ../subiquitycore/ui/views/network_configure_interface.py:91 +msgid "Do not use" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:85 +msgid "Use a static IPv6 configuration" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:88 +msgid "Use DHCPv6 on this interface" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_interface.py:102 +msgid "Configure WIFI settings" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:67 +msgid "Subnet:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:68 +msgid "Address:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:69 +msgid "Gateway:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:70 +msgid "Name servers:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:70 +msgid "IP addresses, comma separated" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:71 +msgid "Search domains:" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:71 +msgid "Domains, comma separated" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:122 +#, python-format +msgid "CIDR e.g. %s" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:158 +msgid "Set this as default gateway" +msgstr "" + +#: ../subiquitycore/ui/views/network_configure_manual_interface.py:161 +msgid "This will be your default gateway" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:51 +msgid "Applying network config" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:65 +#, python-format +msgid "Associated to '%s', will associate to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:67 +#, python-format +msgid "Associated to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:69 +#, python-format +msgid "No access point configured, but associated to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:72 +#, python-format +msgid "Will associate to '%s'" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:74 +msgid "No access point configured" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:94 +#, python-format +msgid "Will use DHCP for IPv%s, currently has address%%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:95 +#, python-format +msgid "Will use DHCP for IPv%s" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:98 +#, python-format +msgid "Using static address%%s for IPv%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:99 +#, python-format +msgid "Will use static address%%s for IPv%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:101 +#, python-format +msgid "Currently has address%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:104 +#, python-format +msgid "Has no IPv%s configuration, currently has address%%s:" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:106 +#, python-format +msgid "IPv%s is not configured" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:165 +msgid "Not connected" +msgstr "" + +#: ../subiquitycore/ui/views/network.py:204 +#, python-format +msgid " IPv4 default route %s." +msgstr "" + +#: ../subiquity/models/installpath.py:31 +msgid "Install Ubuntu" +msgstr "" + +#: ../subiquity/ui/mount.py:53 +msgid "other" +msgstr "другое" + +#: ../subiquity/ui/mount.py:55 +msgid "leave unmounted" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:47 +msgid "" +"\n" +"Selecting Continue below will result of the loss of data on the disks " +"selected to be formatted.\n" +"\n" +"Are you sure you want to continue?\n" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:63 +msgid "Confirm destructive action" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:80 +msgid "FILE SYSTEM SUMMARY" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:84 +msgid "AVAILABLE DEVICES" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:208 +msgid "ADD/EDIT PARTITIONS" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:211 +msgid "ADD FIRST PARTITION" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:214 +msgid "EDIT PARTITIONS" +msgstr "" + +#: ../subiquity/ui/views/filesystem/filesystem.py:222 +msgid "No disks available." +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:33 +msgid "" +"The installer can guide you through partitioning a disk or, if you prefer, " +"you can do it manually. If you choose guided partitioning you will still " +"have a chance to review and modify the results." +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:42 +msgid "Guided" +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:43 +msgid "Manual" +msgstr "" + +#: ../subiquity/ui/views/filesystem/guided.py:78 +msgid "Choose the disk to install to:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:49 +msgid "Your name:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:51 +msgid "Your server's name:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:52 +msgid "The name it uses when it talks to other computers." +msgstr "" + +#: ../subiquity/ui/views/identity.py:53 +msgid "Pick a username:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:54 +msgid "Choose a password:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:55 +msgid "Confirm your password:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:57 +msgid "Import SSH identity:" +msgstr "" + +#: ../subiquity/ui/views/identity.py:58 +msgid "" +"Input your SSH user id from Ubuntu SSO (sso:email), Launchpad (lp:username) " +"or Github (gh:username)." +msgstr "" + +#: ../subiquity/ui/views/identity.py:63 +msgid "Real name must not be empty." +msgstr "" + +#: ../subiquity/ui/views/identity.py:65 +msgid "Realname too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/identity.py:69 +msgid "Server name must not be empty" +msgstr "" + +#: ../subiquity/ui/views/identity.py:72 +msgid "Server name too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/identity.py:76 +msgid "Username missing" +msgstr "" + +#: ../subiquity/ui/views/identity.py:79 +msgid "Username too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/identity.py:85 +msgid "Password must be set" +msgstr "" + +#: ../subiquity/ui/views/identity.py:89 +msgid "Passwords do not match" +msgstr "" + +#: ../subiquity/ui/views/identity.py:94 +msgid "SSH id too long, must be < " +msgstr "" + +#: ../subiquity/ui/views/installprogress.py:65 +msgid "Reboot Now" +msgstr "" + +#: ../subiquity/ui/views/installprogress.py:68 +msgid "Quit Installer" +msgstr "" + +#: ../subiquity/ui/views/welcome.py:40 +msgid "(More language choices will appear in time)" +msgstr "" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..03d88005 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[build_i18n] +domain=subiquity diff --git a/setup.py b/setup.py index 0a2f4cc8..ee6422f4 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,8 @@ Ubuntu Server Installer """ from setuptools import setup, find_packages +from DistUtilsExtra.command import build_extra +from DistUtilsExtra.command import build_i18n import os import sys @@ -43,4 +45,6 @@ setup(name='subiquity', url='https://github.com/CanonicalLtd/subiquity', license="AGPLv3+", packages=find_packages(exclude=["tests"]), + cmdclass={'build': build_extra.build_extra, + 'build_i18n': build_i18n.build_i18n, }, data_files=[]) diff --git a/snapcraft.yaml b/snapcraft.yaml index 02ea0952..44d7eacf 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -15,8 +15,8 @@ apps: parts: subiquity: plugin: python - build-packages: [python-setuptools, pkg-config, lsb-release] - stage-packages: [curtin, lsb-release] + build-packages: [pkg-config, lsb-release, python3-distutils-extra] + stage-packages: [curtin, lsb-release, python3-distutils-extra] python-packages: - urwid - pyyaml diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py index ef8f7867..d5fe43d8 100644 --- a/subiquity/controllers/filesystem.py +++ b/subiquity/controllers/filesystem.py @@ -64,22 +64,22 @@ class FilesystemController(BaseController): if self.model.any_configuration_done(): self.manual() else: - title = "Filesystem setup" - footer = ("Choose guided or manual partitioning") + title = _("Filesystem setup") + footer = (_("Choose guided or manual partitioning")) self.ui.set_header(title) self.ui.set_footer(footer, 30) self.ui.set_body(GuidedFilesystemView(self.model, self)) def manual(self): - title = "Filesystem setup" - footer = ("Select available disks to format and mount") + title = _("Filesystem setup") + footer = (_("Select available disks to format and mount")) self.ui.set_header(title) self.ui.set_footer(footer, 30) self.ui.set_body(FilesystemView(self.model, self)) def guided(self): - title = "Filesystem setup" - footer = ("Choose the installation target") + title = _("Filesystem setup") + footer = (_("Choose the installation target")) self.ui.set_header(title) self.ui.set_footer(footer, 30) self.ui.set_body(GuidedDiskSelectionView(self.model, self)) @@ -93,9 +93,9 @@ class FilesystemController(BaseController): self.signal.emit_signal('prev-screen') def filesystem_error(self, error_fname): - title = "Filesystem error" - footer = ("Error while installing Ubuntu") - error_msg = "Failed to obtain write permissions to /tmp" + title = _("Filesystem error") + footer = (_("Error while installing Ubuntu")) + error_msg = _("Failed to obtain write permissions to /tmp") self.ui.set_header(title) self.ui.set_footer(footer, 30) self.ui.set_body(ErrorView(self.signal, error_msg)) @@ -137,9 +137,9 @@ class FilesystemController(BaseController): # Filesystem/Disk partition ----------------------------------------------- def partition_disk(self, disk): log.debug("In disk partition view, using {} as the disk.".format(disk.serial)) - title = ("Partition, format, and mount {}".format(disk.serial)) - footer = ("Partition the disk, or format the entire device " - "without partitions") + title = (_("Partition, format, and mount {}").format(disk.serial)) + footer = (_("Partition the disk, or format the entire device " + "without partitions")) self.ui.set_header(title) self.ui.set_footer(footer) dp_view = DiskPartitionView(self.model, self, disk) @@ -148,14 +148,14 @@ class FilesystemController(BaseController): def add_disk_partition(self, disk): log.debug("Adding partition to {}".format(disk)) - footer = ("Select whole disk, or partition, to format and mount.") + footer = _("Select whole disk, or partition, to format and mount.") self.ui.set_footer(footer) adp_view = PartitionView(self.model, self, disk) self.ui.set_body(adp_view) def edit_partition(self, disk, partition): log.debug("Editing partition {}".format(partition)) - footer = ("Edit partition details format and mount.") + footer = _("Edit partition details format and mount.") self.ui.set_footer(footer) adp_view = PartitionView(self.model, self, disk, partition) self.ui.set_body(adp_view) @@ -262,20 +262,20 @@ class FilesystemController(BaseController): self.ui.set_body(DummyView(self.signal)) def create_volume_group(self, *args, **kwargs): - title = ("Create Logical Volume Group (\"LVM2\") disk") - footer = ("ENTER on a disk will show detailed " + title = _("Create Logical Volume Group (\"LVM2\") disk") + footer = _("ENTER on a disk will show detailed " "information for that disk") - excerpt = ("Use SPACE to select disks to form your LVM2 volume group, " + excerpt = _("Use SPACE to select disks to form your LVM2 volume group, " "and then specify the Volume Group name. ") self.ui.set_header(title, excerpt) self.ui.set_footer(footer) self.ui.set_body(LVMVolumeGroupView(self.model, self.signal)) def create_raid(self, *args, **kwargs): - title = ("Create software RAID (\"MD\") disk") - footer = ("ENTER on a disk will show detailed " + title = _("Create software RAID (\"MD\") disk") + footer = _("ENTER on a disk will show detailed " "information for that disk") - excerpt = ("Use SPACE to select disks to form your RAID array, " + excerpt = _("Use SPACE to select disks to form your RAID array, " "and then specify the RAID parameters. Multiple-disk " "arrays work best when all the disks in an array are " "the same size and speed.") @@ -285,10 +285,10 @@ class FilesystemController(BaseController): self.signal)) def create_bcache(self, *args, **kwargs): - title = ("Create hierarchical storage (\"bcache\") disk") - footer = ("ENTER on a disk will show detailed " + title = _("Create hierarchical storage (\"bcache\") disk") + footer = _("ENTER on a disk will show detailed " "information for that disk") - excerpt = ("Use SPACE to select a cache disk and a backing disk" + excerpt = _("Use SPACE to select a cache disk and a backing disk" " to form your bcache device.") self.ui.set_header(title, excerpt) @@ -303,8 +303,8 @@ class FilesystemController(BaseController): def format_entire(self, disk): log.debug("format_entire {}".format(disk.serial)) - header = ("Format and/or mount {}".format(disk.serial)) - footer = ("Format or mount whole disk.") + header = (_("Format and/or mount {}").format(disk.serial)) + footer = _("Format or mount whole disk.") self.ui.set_header(header) self.ui.set_footer(footer) afv_view = FormatEntireView(self.model, self, disk, lambda : self.partition_disk(disk)) @@ -313,11 +313,11 @@ class FilesystemController(BaseController): def format_mount_partition(self, partition): log.debug("format_entire {}".format(partition)) if partition.fs() is not None: - header = ("Mount partition {} of {}".format(partition.number, partition.device.serial)) - footer = ("Mount partition.") + header = (_("Mount partition {} of {}").format(partition.number, partition.device.serial)) + footer = _("Mount partition.") else: - header = ("Format and mount partition {} of {}".format(partition.number, partition.device.serial)) - footer = ("Format and mount partition.") + header = (_("Format and mount partition {} of {}").format(partition.number, partition.device.serial)) + footer = _("Format and mount partition.") self.ui.set_header(header) self.ui.set_footer(footer) afv_view = FormatEntireView(self.model, self, partition, self.default) @@ -382,7 +382,7 @@ class FilesystemController(BaseController): result = template.format(**dinfo) log.debug('calling DiskInfoView()') disk_info_view = DiskInfoView(self.model, self, disk, result) - footer = ('Select next or previous disks with n and p') + footer = _('Select next or previous disks with n and p') self.ui.set_footer(footer, 30) self.ui.set_body(disk_info_view) diff --git a/subiquity/controllers/identity.py b/subiquity/controllers/identity.py index 64298b2a..e16eebb2 100644 --- a/subiquity/controllers/identity.py +++ b/subiquity/controllers/identity.py @@ -33,8 +33,8 @@ class IdentityController(BaseController): self.model = IdentityModel(self.opts) def default(self): - title = "Profile setup" - excerpt = ("Enter the username and password (or ssh identity) you will use to log in to the system.") + title = _("Profile setup") + excerpt = _("Enter the username and password (or ssh identity) you will use to log in to the system.") footer = "" self.ui.set_header(title, excerpt) self.ui.set_footer(footer, 40) diff --git a/subiquity/controllers/installpath.py b/subiquity/controllers/installpath.py index b2cb43b8..4e58049d 100644 --- a/subiquity/controllers/installpath.py +++ b/subiquity/controllers/installpath.py @@ -42,11 +42,11 @@ class InstallpathController(BaseController): def installpath(self): title = "Ubuntu %s"%(lsb_release.get_distro_information()['RELEASE'],) - excerpt = ("Welcome to Ubuntu! The world's favorite platform " + excerpt = _("Welcome to Ubuntu! The world's favorite platform " "for clouds, clusters, and amazing internet things. " "This is the installer for Ubuntu on servers and " "internet devices.") - footer = ("Use UP, DOWN arrow keys, and ENTER, to " + footer = _("Use UP, DOWN arrow keys, and ENTER, to " "navigate options") self.ui.set_header(title, excerpt) diff --git a/subiquity/controllers/installprogress.py b/subiquity/controllers/installprogress.py index 195e419a..6539da00 100644 --- a/subiquity/controllers/installprogress.py +++ b/subiquity/controllers/installprogress.py @@ -71,9 +71,9 @@ class InstallProgressController(BaseController): def curtin_error(self): log.debug('curtin_error') - title = ('An error occurred during installation') - self.ui.set_header(title, 'Please report this error in Launchpad') - self.ui.set_footer("An error has occurred.", 100) + title = _('An error occurred during installation') + self.ui.set_header(title, _('Please report this error in Launchpad')) + self.ui.set_footer(_("An error has occurred."), 100) if self.progress_view is not None: self.progress_view.set_status(('info_error', "An error has occurred")) self.progress_view.show_complete() @@ -134,7 +134,7 @@ class InstallProgressController(BaseController): self.install_state = InstallState.RUNNING_POSTINSTALL if self.progress_view is not None: self.progress_view.clear_log_tail() - self.progress_view.set_status("Running postinstall step") + self.progress_view.set_status(_("Running postinstall step")) self.start_tail_proc() if self.opts.dry_run: log.debug("Installprogress: this is a dry-run") @@ -162,9 +162,9 @@ class InstallProgressController(BaseController): return log.debug('After curtin postinstall OK') self.install_state = InstallState.DONE_POSTINSTALL - self.ui.set_header("Installation complete!", "") + self.ui.set_header(_("Installation complete!"), "") self.ui.set_footer("", 100) - self.progress_view.set_status("Finished install!") + self.progress_view.set_status(_("Finished install!")) self.progress_view.show_complete() def update_log_tail(self): @@ -212,9 +212,9 @@ class InstallProgressController(BaseController): def default(self): log.debug('show_progress called') - title = ("Installing system") - excerpt = ("Please wait for the installation to finish.") - footer = ("Thank you for using Ubuntu!") + title = _("Installing system") + excerpt = _("Please wait for the installation to finish.") + footer = _("Thank you for using Ubuntu!") self.ui.set_header(title, excerpt) self.ui.set_footer(footer, 90) self.progress_view = ProgressView(self.model, self) @@ -223,9 +223,9 @@ class InstallProgressController(BaseController): self.ui.set_body(self.progress_view) return if self.install_state < InstallState.RUNNING_POSTINSTALL: - self.progress_view.set_status("Running install step") + self.progress_view.set_status(_("Running install step")) else: - self.progress_view.set_status("Running postinstall step") + self.progress_view.set_status(_("Running postinstall step")) self.ui.set_body(self.progress_view) self.start_tail_proc() diff --git a/subiquity/controllers/welcome.py b/subiquity/controllers/welcome.py index ca15c0ab..e0711bad 100644 --- a/subiquity/controllers/welcome.py +++ b/subiquity/controllers/welcome.py @@ -27,9 +27,9 @@ class WelcomeController(BaseController): self.model = WelcomeModel() def default(self): - title = "Wilkommen! Bienvenue! Welcome! Zdrastvutie! Welkom!" - excerpt = "Please choose your preferred language" - footer = ("Use UP, DOWN and ENTER keys to select your language.") + title = "Willkommen! Bienvenue! Welcome! Добро пожаловать! Welkom!" + excerpt = _("Please choose your preferred language") + footer = _("Use UP, DOWN and ENTER keys to select your language.") self.ui.set_header(title, excerpt) self.ui.set_footer(footer) view = WelcomeView(self.model, self) diff --git a/subiquity/models/installpath.py b/subiquity/models/installpath.py index a8ccede2..0e30be25 100644 --- a/subiquity/models/installpath.py +++ b/subiquity/models/installpath.py @@ -28,7 +28,7 @@ class InstallpathModel(object): # TODO: Re-enable once available install_paths = [ - ('Install Ubuntu', 'installpath:install-ubuntu'), + (_('Install Ubuntu'), 'installpath:install-ubuntu'), # ('Install MAAS Region Server', 'installpath:maas-region-server'), # ('Install MAAS Cluster Server', 'installpath:maas-cluster-server'), # ('Test installation media', 'installpath:test-media'), diff --git a/subiquity/models/welcome.py b/subiquity/models/welcome.py index bc06f89d..7478b80f 100644 --- a/subiquity/models/welcome.py +++ b/subiquity/models/welcome.py @@ -13,8 +13,9 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import gettext import logging - +from subiquitycore import i18n log = logging.getLogger('subiquity.models.welcome') @@ -23,13 +24,26 @@ class WelcomeModel(object): """ Model representing language selection """ - supported_languages = [ - ('English', 'en_US'), - ] + supported_languages = [('en_US', 'English'), ('ru_RU', 'Russian')] selected_language = None def get_languages(self): - return self.supported_languages + languages = [] + for code, name in self.supported_languages: + label = name + native = name + if gettext.find('iso_639_3'): + cur_lang = gettext.translation('iso_639_3') + label = cur_lang.gettext(name).capitalize() + if gettext.find('iso_639_3', languages=[code]): + native_lang = gettext.translation('iso_639_3', languages=[code]) + native = native_lang.gettext(name).capitalize() + languages.append((code, label, native)) + return languages + + def switch_language(self, code): + self.selected_language = code + i18n.switch_language(code) def __repr__(self): return "".format(self.selected_language) diff --git a/subiquity/ui/mount.py b/subiquity/ui/mount.py index 8a4f40cb..06c0c205 100644 --- a/subiquity/ui/mount.py +++ b/subiquity/ui/mount.py @@ -50,9 +50,9 @@ class MountSelector(WidgetWrap): opts.append(("%-*s (%s)"%(max_len, mnt, devpath), False)) if first_opt is None: first_opt = len(opts) - opts.append(('other', True, OTHER)) + opts.append((_('other'), True, OTHER)) opts.append(('---', False)), - opts.append(('leave unmounted', True, LEAVE_UNMOUNTED)) + opts.append((_('leave unmounted'), True, LEAVE_UNMOUNTED)) self._selector = Selector(opts, first_opt) connect_signal(self._selector, 'select', self._select_mount) self._other = _MountEditor(edit_text='/') diff --git a/subiquity/ui/views/filesystem/filesystem.py b/subiquity/ui/views/filesystem/filesystem.py index 0ff87853..23f59c08 100644 --- a/subiquity/ui/views/filesystem/filesystem.py +++ b/subiquity/ui/views/filesystem/filesystem.py @@ -44,12 +44,12 @@ from subiquity.models.filesystem import humanize_size log = logging.getLogger('subiquity.ui.filesystem.filesystem') -confirmation_text = """\ +confirmation_text = _("""\ Selecting Continue below will begin the installation process and \ result in the loss of data on the disks selected to be formatted. Are you sure you want to continue? -""" +""") class FilesystemConfirmationView(WidgetWrap): def __init__(self, parent, controller): @@ -57,11 +57,11 @@ class FilesystemConfirmationView(WidgetWrap): self.controller = controller pile = Pile([ UrwidPadding(Text(confirmation_text), left=2, right=2), - Padding.fixed_15(cancel_btn(label="No", on_press=self.cancel)), + Padding.fixed_15(cancel_btn(label=_("No"), on_press=self.cancel)), Padding.fixed_15(danger_btn(on_press=self.ok)), Text(""), ]) - lb = LineBox(pile, title="Confirm destructive action") + lb = LineBox(pile, title=_("Confirm destructive action")) super().__init__(Padding.center_75(lb)) def ok(self, sender): @@ -78,11 +78,11 @@ class FilesystemView(BaseView): self.controller = controller self.items = [] self.body = [ - Text("FILE SYSTEM SUMMARY"), + Text(_("FILE SYSTEM SUMMARY")), Text(""), Padding.push_4(self._build_filesystem_list()), Text(""), - Text("AVAILABLE DEVICES"), + Text(_("AVAILABLE DEVICES")), Text(""), Padding.push_4(self._build_available_inputs()), #self._build_menu(), @@ -206,13 +206,13 @@ class FilesystemView(BaseView): free = disk.free percent = int(100*free/size) if disk.available and disk.used > 0 and percent > 0: - label = "ADD/EDIT PARTITIONS" + label = _("ADD/EDIT PARTITIONS") size = "{:>9} ({}%) free".format(humanize_size(free), percent) elif disk.available and percent > 0: - label = "ADD FIRST PARTITION" + label = _("ADD FIRST PARTITION") size = "" else: - label = "EDIT PARTITIONS" + label = _("EDIT PARTITIONS") size = "" col2( menu_btn(label=label, on_press=self.click_disk, user_arg=disk), @@ -220,7 +220,7 @@ class FilesystemView(BaseView): if len(inputs) == 1: return Pile([Color.info_minor( - Text("No disks available."))]) + Text(_("No disks available.")))]) return Pile(inputs) diff --git a/subiquity/ui/views/filesystem/guided.py b/subiquity/ui/views/filesystem/guided.py index 80267cc6..06c3b0d5 100644 --- a/subiquity/ui/views/filesystem/guided.py +++ b/subiquity/ui/views/filesystem/guided.py @@ -30,17 +30,17 @@ from subiquitycore.view import BaseView from subiquity.models.filesystem import humanize_size -text = """The installer can guide you through partitioning a disk or, if \ +text = _("""The installer can guide you through partitioning a disk or, if \ you prefer, you can do it manually. If you choose guided partitioning you \ -will still have a chance to review and modify the results.""" +will still have a chance to review and modify the results.""") class GuidedFilesystemView(BaseView): def __init__(self, model, controller): self.controller = controller - guided = ok_btn(label="Guided", on_press=self.guided) - manual = ok_btn(label="Manual", on_press=self.manual) + guided = ok_btn(label=_("Guided"), on_press=self.guided) + manual = ok_btn(label=_("Manual"), on_press=self.manual) cancel = cancel_btn(on_press=self.cancel) lb = ListBox([ Padding.center_70(Text(text)), @@ -75,7 +75,7 @@ class GuidedDiskSelectionView(BaseView): on_press=self.choose_disk, user_arg=disk) disks.append(disk_btn) lb = ListBox([ - Padding.center_70(Text("Choose the disk to install to:")), + Padding.center_70(Text(_("Choose the disk to install to:"))), Padding.center_70(Text("")), Padding.center_70(Pile(disks)), Padding.center_70(Text("")), diff --git a/subiquity/ui/views/identity.py b/subiquity/ui/views/identity.py index 36fe4585..935e71de 100644 --- a/subiquity/ui/views/identity.py +++ b/subiquity/ui/views/identity.py @@ -46,52 +46,52 @@ PasswordField = simple_field(PasswordEditor) class IdentityForm(Form): - realname = RealnameField("Your name:") + realname = RealnameField(_("Your name:")) hostname = UsernameField( - "Your server's name:", - help="The name it uses when it talks to other computers.") - username = UsernameField("Pick a username:") - password = PasswordField("Choose a password:") - confirm_password = PasswordField("Confirm your password:") + _("Your server's name:"), + help=_("The name it uses when it talks to other computers.")) + username = UsernameField(_("Pick a username:")) + password = PasswordField(_("Choose a password:")) + confirm_password = PasswordField(_("Confirm your password:")) ssh_import_id = StringField( - "Import SSH identity:", - help=("Input your SSH user id from Ubuntu SSO (sso:email), " - "Launchpad (lp:username) or Github (gh:username).")) + _("Import SSH identity:"), + help=(_("Input your SSH user id from Ubuntu SSO (sso:email), " + "Launchpad (lp:username) or Github (gh:username)."))) def validate_realname(self): if len(self.realname.value) < 1: - return "Real name must not be empty." + return _("Real name must not be empty.") if len(self.realname.value) > REALNAME_MAXLEN: - return "Realname too long, must be < " + str(REALNAME_MAXLEN) + return _("Realname too long, must be < ") + str(REALNAME_MAXLEN) def validate_hostname(self): if len(self.hostname.value) < 1: - return "Server name must not be empty" + return _("Server name must not be empty") if len(self.hostname.value) > HOSTNAME_MAXLEN: - return "Server name too long, must be < " + str(HOSTNAME_MAXLEN) + return _("Server name too long, must be < ") + str(HOSTNAME_MAXLEN) def validate_username(self): if len(self.username.value) < 1: - return "Username missing" + return _("Username missing") if len(self.username.value) > USERNAME_MAXLEN: - return "Username too long, must be < " + str(USERNAME_MAXLEN) + return _("Username too long, must be < ") + str(USERNAME_MAXLEN) def validate_password(self): # XXX we should not require a password if an ssh identity is provided # Form doesn't support form-wide validation yet though, oops. if len(self.password.value) < 1: - return "Password must be set" + return _("Password must be set") def validate_confirm_password(self): if self.password.value != self.confirm_password.value: - return "Passwords do not match" + return _("Passwords do not match") self.password.validate() def validate_ssh_import_id(self): if len(self.ssh_import_id.value) > SSH_IMPORT_MAXLEN: - return "SSH id too long, must be < " + str(SSH_IMPORT_MAXLEN) + return _("SSH id too long, must be < ") + str(SSH_IMPORT_MAXLEN) class IdentityView(BaseView): diff --git a/subiquity/ui/views/installprogress.py b/subiquity/ui/views/installprogress.py index 4d2e5356..6dd95b96 100644 --- a/subiquity/ui/views/installprogress.py +++ b/subiquity/ui/views/installprogress.py @@ -62,10 +62,10 @@ class ProgressView(BaseView): def show_complete(self): w = Padding.fixed_20( - ok_btn(label="Reboot Now", on_press=self.reboot)) + ok_btn(label=_("Reboot Now"), on_press=self.reboot)) z = Padding.fixed_20( - cancel_btn(label="Quit Installer", on_press=self.quit)) + cancel_btn(label=_("Quit Installer"), on_press=self.quit)) new_focus = len(self.pile.contents) self.pile.contents.append((w, self.pile.options('pack'))) diff --git a/subiquity/ui/views/welcome.py b/subiquity/ui/views/welcome.py index 9621eb80..1dcd8035 100644 --- a/subiquity/ui/views/welcome.py +++ b/subiquity/ui/views/welcome.py @@ -19,7 +19,7 @@ Welcome provides user with language selection """ import logging -from urwid import BoxAdapter, Text +from urwid import BoxAdapter, Text, connect_signal from subiquitycore.ui.lists import SimpleList from subiquitycore.ui.buttons import menu_btn from subiquitycore.ui.container import ListBox @@ -37,16 +37,16 @@ class WelcomeView(BaseView): super().__init__(ListBox([ Padding.center_50(self._build_model_inputs()), Text(""), - Padding.center_79(Text("(More language choices will appear in time)"))])) + Padding.center_79(Text(_("(More language choices will appear in time)")))])) def _build_model_inputs(self): sl = [] - for lang, code in self.model.get_languages(): - sl.append(menu_btn(label=lang, on_press=self.confirm, user_arg=code)) + for code, label, native in self.model.get_languages(): + sl.append(menu_btn(label=native, on_press=self.confirm, user_arg=code)) return BoxAdapter(SimpleList(sl), height=len(sl)) def confirm(self, sender, code): - self.model.selected_language = code + self.model.switch_language(code) log.debug('calling installpath') self.controller.done() diff --git a/subiquitycore/controllers/network.py b/subiquitycore/controllers/network.py index f04f1c1a..2fa6e451 100644 --- a/subiquitycore/controllers/network.py +++ b/subiquitycore/controllers/network.py @@ -344,11 +344,11 @@ class NetworkController(BaseController): self.signal.emit_signal('prev-screen') def default(self): - title = "Network connections" - excerpt = ("Configure at least one interface this server can use to talk to " + title = _("Network connections") + excerpt = _("Configure at least one interface this server can use to talk to " "other machines, and which preferably provides sufficient access for " "updates.") - footer = ("Select an interface to configure it or select Done to continue") + footer = _("Select an interface to configure it or select Done to continue") self.ui.set_header(title, excerpt) self.ui.set_footer(footer, 20) self.ui.set_body(NetworkView(self.model, self)) diff --git a/subiquitycore/i18n.py b/subiquitycore/i18n.py new file mode 100644 index 00000000..8c925a6b --- /dev/null +++ b/subiquitycore/i18n.py @@ -0,0 +1,40 @@ +# Copyright 2017 Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +import gettext +import os +import syslog + +syslog.syslog('i18n file is ' + __file__) +localedir = '/usr/share/locale' +if __file__.startswith('/snap/'): + localedir = os.path.realpath(__file__ + '/../../../../../share/locale') +build_mo = os.path.realpath(__file__ + '/../../build/mo/') +if os.path.isdir(build_mo): + localedir = build_mo +syslog.syslog('Final localedir is ' + localedir) + +def switch_language(code='en_US'): + if code != 'en_US' and 'FAKE_TRANSLATE' in os.environ: + import builtins + builtins.__dict__['_'] = lambda a: '_(%s)' % a + elif code: + translation = gettext.translation('subiquity', localedir=localedir, languages=[code]) + translation.install() + +switch_language() + +__all__ = ['switch_language'] diff --git a/subiquitycore/ui/interactive.py b/subiquitycore/ui/interactive.py index 369fe693..bb2f2cb3 100644 --- a/subiquitycore/ui/interactive.py +++ b/subiquitycore/ui/interactive.py @@ -111,7 +111,7 @@ class YesNo(Selector): """ Yes/No selector """ def __init__(self): - opts = ['Yes', 'No'] + opts = [_('Yes'), _('No')] super().__init__(opts) @@ -119,7 +119,7 @@ class _HelpDisplay(WidgetWrap): def __init__(self, closer, help_text): self._closer = closer button = close_btn(on_press=lambda btn:self._closer()) - super().__init__(LineBox(Pile([Text(help_text), Padding.fixed_10(button)]), title="Help")) + super().__init__(LineBox(Pile([Text(help_text), Padding.fixed_10(button)]), title=_("Help"))) class Help(WidgetWrap): diff --git a/subiquitycore/ui/views/network.py b/subiquitycore/ui/views/network.py index a4c57b50..4121611f 100644 --- a/subiquitycore/ui/views/network.py +++ b/subiquitycore/ui/views/network.py @@ -48,7 +48,7 @@ class ApplyingConfigWidget(WidgetWrap): current=0, done=step_count) box = LineBox(Pile([self.bar, Padding.fixed_10(button)]), - title="Applying network config") + title=_("Applying network config")) super().__init__(box) def advance(self): @@ -62,16 +62,16 @@ def _build_wifi_info(dev): if dev.actual_ssid is not None: if dev.configured_ssid is not None: if dev.actual_ssid != dev.configured_ssid: - r.append(Text("Associated to '%s', will associate to '%s'"%(dev.actual_ssid, dev.configured_ssid))) + r.append(Text(_("Associated to '%s', will associate to '%s'" % (dev.actual_ssid, dev.configured_ssid)))) else: - r.append(Text("Associated to '" + dev.actual_ssid + "'")) + r.append(Text(_("Associated to '%s'" % dev.actual_ssid))) else: - r.append(Text("No access point configured, but associated to '%s'"%(dev.actual_ssid,))) + r.append(Text(_("No access point configured, but associated to '%s'" % dev.actual_ssid))) else: if dev.configured_ssid is not None: - r.append(Text("Will associate to '" + dev.configured_ssid + "'")) + r.append(Text(_("Will associate to '%s'" % dev.configured_ssid))) else: - r.append(Text("No access point configured")) + r.append(Text(_("No access point configured"))) return r def _format_address_list(label, addresses): @@ -91,19 +91,19 @@ def _build_gateway_ip_info_for_version(dev, version): configured_ip_addresses = dev.configured_ip_addresses_for_version(version) if dev.dhcp_for_version(version): if dev.actual_ip_addresses: - return _format_address_list("Will use DHCP for IPv%s, currently has address%%s:"%(version,), actual_ip_addresses) - return [Text("Will use DHCP for IPv%s"%(version,))] + return _format_address_list(_("Will use DHCP for IPv%s, currently has address%%s:" % version), actual_ip_addresses) + return [Text(_("Will use DHCP for IPv%s" % version))] elif configured_ip_addresses: if sorted(actual_ip_addresses) == sorted(configured_ip_addresses): - return _format_address_list("Using static address%%s for IPv%s:"%(version,), actual_ip_addresses) - p = _format_address_list("Will use static address%%s for IPv%s:"%(version,), configured_ip_addresses) + return _format_address_list(_("Using static address%%s for IPv%s:" % version), actual_ip_addresses) + p = _format_address_list(_("Will use static address%%s for IPv%s:" % version), configured_ip_addresses) if actual_ip_addresses: - p.extend(_format_address_list("Currently has address%s:", actual_ip_addresses)) + p.extend(_format_address_list(_("Currently has address%s:"), actual_ip_addresses)) return p elif actual_ip_addresses: - return _format_address_list("Has no IPv%s configuration, currently has address%%s:"%(version,), actual_ip_addresses) + return _format_address_list(_("Has no IPv%s configuration, currently has address%%s:" % version), actual_ip_addresses) else: - return [Text("IPv%s is not configured"%(version,))] + return [Text(_("IPv%s is not configured" % version))] class NetworkView(BaseView): @@ -162,7 +162,7 @@ class NetworkView(BaseView): if dev.type == 'wlan': col_2.extend(_build_wifi_info(dev)) if len(dev.actual_ip_addresses) == 0 and dev.type == 'eth' and not dev.is_connected: - col_2.append(Color.info_primary(Text("Not connected"))) + col_2.append(Color.info_primary(Text(_("Not connected")))) col_2.extend(_build_gateway_ip_info_for_version(dev, 4)) col_2.extend(_build_gateway_ip_info_for_version(dev, 6)) @@ -170,8 +170,10 @@ class NetworkView(BaseView): template = '' if dev.hwaddr: template += '{} '.format(dev.hwaddr) + ## TODO is this to translate? if dev.is_bond_slave: template += '(Bonded) ' + ## TODO to check if this is affected by translations if not dev.vendor.lower().startswith('unknown'): vendor = textwrap.wrap(dev.vendor, 15)[0] template += '{} '.format(vendor) @@ -199,7 +201,7 @@ class NetworkView(BaseView): v4_route_source = "via " + self.model.default_v4_gateway default_v4_route_w = Color.info_minor( - Text(" IPv4 default route " + v4_route_source + ".")) + Text(_(" IPv4 default route %s." % v4_route_source))) labels.append(default_v4_route_w) if self.model.default_v6_gateway is not None: diff --git a/subiquitycore/ui/views/network_configure_interface.py b/subiquitycore/ui/views/network_configure_interface.py index 15c3b5e3..b864b9c8 100644 --- a/subiquitycore/ui/views/network_configure_interface.py +++ b/subiquitycore/ui/views/network_configure_interface.py @@ -63,11 +63,11 @@ class NetworkConfigureInterfaceView(BaseView): button_padding = 70 buttons = [ - menu_btn(label="Use a static IPv4 configuration", + menu_btn(label=_("Use a static IPv4 configuration"), on_press=self.show_ipv4_configuration), - menu_btn(label="Use DHCPv4 on this interface", + menu_btn(label=_("Use DHCPv4 on this interface"), on_press=self.enable_dhcp4), - menu_btn(label="Do not use", + menu_btn(label=_("Do not use"), on_press=self.clear_ipv4), ] @@ -80,11 +80,11 @@ class NetworkConfigureInterfaceView(BaseView): button_padding = 70 buttons = [ - menu_btn(label="Use a static IPv6 configuration", + menu_btn(label=_("Use a static IPv6 configuration"), on_press=self.show_ipv6_configuration), - menu_btn(label="Use DHCPv6 on this interface", + menu_btn(label=_("Use DHCPv6 on this interface"), on_press=self.enable_dhcp6), - menu_btn(label="Do not use", + menu_btn(label=_("Do not use"), on_press=self.clear_ipv6), ] @@ -95,7 +95,7 @@ class NetworkConfigureInterfaceView(BaseView): def _build_wifi_config(self): - btn = menu_btn(label="Configure WIFI settings", on_press=self.show_wlan_configuration) + btn = menu_btn(label=_("Configure WIFI settings"), on_press=self.show_wlan_configuration) return [Padding.left_70(btn)] def _build_buttons(self): diff --git a/subiquitycore/ui/views/network_configure_manual_interface.py b/subiquitycore/ui/views/network_configure_manual_interface.py index e9dc59dc..556a77d5 100644 --- a/subiquitycore/ui/views/network_configure_manual_interface.py +++ b/subiquitycore/ui/views/network_configure_manual_interface.py @@ -64,11 +64,11 @@ class NetworkConfigForm(Form): self.ip_address_cls = fam['address_cls'] self.ip_network_cls = fam['network_cls'] - subnet = IPField("Subnet:", has_mask=True) - address = IPField("Address:") - gateway = IPField("Gateway:") - nameservers = StringField("Name servers:", help="IP addresses, comma separated") - searchdomains = StringField("Search domains:", help="Domains, comma separated") + subnet = IPField(_("Subnet:"), has_mask=True) + address = IPField(_("Address:")) + gateway = IPField(_("Gateway:")) + nameservers = StringField(_("Name servers:"), help=_("IP addresses, comma separated")) + searchdomains = StringField(_("Search domains:"), help=_("Domains, comma separated")) def clean_subnet(self, subnet): log.debug("clean_subnet %r", subnet) @@ -119,7 +119,7 @@ class BaseNetworkConfigureManualView(BaseView): connect_signal(self.form, 'submit', self.done) connect_signal(self.form, 'cancel', self.cancel) - self.form.subnet.help = "CIDR e.g. %s"%(self.example_address,) + self.form.subnet.help = _("CIDR e.g. %s"%(self.example_address,)) configured_addresses = self.dev.configured_ip_addresses_for_version(self.ip_version) if configured_addresses: addr = ipaddress.ip_interface(configured_addresses[0]) @@ -155,10 +155,10 @@ class BaseNetworkConfigureManualView(BaseView): self.is_gateway = self.model.v4_gateway_dev == self.dev.name if not self.is_gateway and len(devs) > 1: - btn = menu_btn(label="Set this as default gateway", + btn = menu_btn(label=_("Set this as default gateway"), on_press=self.set_default_gateway) else: - btn = Text("This will be your default gateway") + btn = Text(_("This will be your default gateway")) return [btn]