diff --git a/CHANGES b/CHANGES index 4d5e19888..cc3a596f9 100644 --- a/CHANGES +++ b/CHANGES @@ -3,7 +3,7 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. -# 3.2.28 (unreleased) # +# 3.2.28 (2020-08-09) # This release contains contributions from (alphabetically by first name): - Anke Boersma @@ -14,9 +14,11 @@ This release contains contributions from (alphabetically by first name): - A new object *Network* is available to QML modules in `io.calamares.core`. It exposes network status through the *hasInternet* property. - Welcome to Tajik translations. The Tajik language has quickly reached - 100% completion. + 100% completion. Thanks Victor! - Welcome to [Interlingue](https://en.wikipedia.org/wiki/Interlingue). - The translation is at an early stage. + The translation is at an early stage. Qt does not support language + code *ie* though, so it may take some time to be integrated (much + like Esperanto wasn't supported until Qt 5.12). ## Modules ## - The *locale* module has been completely redone on the inside. @@ -31,9 +33,15 @@ This release contains contributions from (alphabetically by first name): timezone setting -- this can be useful to avoid both hard-coding an initial zone and doing extra GeoIP lookups, in the case where the live system already does so. #1391 + - The *locale* and *localeq* modules have additional machinery for + timezone lookups; please report cases where clicking on the map + returns an obviously bogus timezone (up until this release, for + instance, Cape Town). - The *users* module no longer accepts `root` as a username. #1462 - The *keyboardq* module is now more inline with the look of the rest of the Calamares modules, use of a background image is removed. + - The *grubcfg* module now understands `/etc/default/grub.d`. #1457 + # 3.2.27 (2020-07-11) # diff --git a/CMakeLists.txt b/CMakeLists.txt index cabd72700..4693e8a84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,8 @@ # === This file is part of Calamares - === # +# SPDX-FileCopyrightText: 2017 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# # Calamares 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 @@ -13,9 +16,6 @@ # You should have received a copy of the GNU General Public License # along with Calamares. If not, see . # -# SPDX-License-Identifier: GPL-3.0-or-later -# License-Filename: LICENSE -# ### # # Generally, this CMakeLists.txt will find all the dependencies for Calamares @@ -49,7 +49,7 @@ project( CALAMARES VERSION 3.2.28 LANGUAGES C CXX ) -set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development +set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development ### OPTIONS # @@ -140,20 +140,18 @@ set( CALAMARES_DESCRIPTION_SUMMARY # TODO: drop the es_ES translation from Transifex # # NOTE: move eo (Esperanto) to _ok once Qt can actually create a -# locale for it. (Qt 5.12.2 can, see check later on). -# NOTE: update these lines by running txstats.py, or copy these four lines -# and prefix each variable name with "p", so that the automatic -# checks for new languages and misspelled ones are done (that is, -# copy these four lines to four backup lines, add "p", and then update -# the original four lines with the current translations). +# locale for it. (Qt 5.12.2 can, see Translation Status section). +# NOTE: move ie (Interlingue) to _ok once Qt supports it. +# NOTE: update these lines by running `txstats.py`, or for full automation +# `txstats.py -e`. See also # # Total 68 languages set( _tx_complete ca fi_FI he hr nl pt_BR sq tg tr_TR uk zh_TW ) set( _tx_good as ast az az_AZ be cs_CZ da de es fr hi hu it_IT ja ko lt ml pt_PT ru sk sv zh_CN ) -set( _tx_ok ar bg el en_GB es_MX es_PR et eu fa gl id ie is mr nb +set( _tx_ok ar bg el en_GB es_MX es_PR et eu fa gl id is mr nb pl ro sl sr sr@latin th ) -set( _tx_incomplete bn ca@valencia eo fr_CH gu kk kn lo lv mk ne_NP +set( _tx_incomplete bn ca@valencia eo fr_CH gu ie kk kn lo lv mk ne_NP ur uz ) ### Required versions @@ -286,14 +284,6 @@ find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Concurrent Core Gui LinguistTool if( WITH_QML ) find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Quick QuickWidgets ) endif() -if( Qt5_VERSION VERSION_GREATER 5.12.1 ) - # At least Qt 5.12.2 seems to support Esperanto in QLocale - if( "eo" IN_LIST _tx_incomplete ) - message(STATUS "Esperanto support since Qt 5.12.2, enabling Esperanto locale") - list( REMOVE_ITEM _tx_incomplete "eo" ) - list( APPEND _tx_ok "eo" ) - endif() -endif() # Optional Qt parts find_package( Qt5DBus CONFIG ) @@ -429,30 +419,22 @@ set(Calamares_WITH_QML ${WITH_QML}) ### Transifex Translation status # -# Construct language lists for use. If there are p_tx* variables, -# then run an extra cmake-time check for consistency of the old -# (p_tx*) and new (_tx*) lists. +# Construct language lists for use. # -set( prev_tx ${p_tx_complete} ${p_tx_good} ${p_tx_ok} ${p_tx_incomplete} ) +if( Qt5_VERSION VERSION_GREATER 5.12.1 ) + # At least Qt 5.12.2 seems to support Esperanto in QLocale + if( "eo" IN_LIST _tx_incomplete ) + message(STATUS "Esperanto support since Qt 5.12.2, enabling Esperanto locale") + list( REMOVE_ITEM _tx_incomplete "eo" ) + list( APPEND _tx_ok "eo" ) + endif() +endif() + set( curr_tx ${_tx_complete} ${_tx_good} ${_tx_ok} ${_tx_incomplete} ) set( tx_errors OFF ) -if ( prev_tx ) - # Gone in new list - foreach( l ${prev_tx} ) - list( FIND curr_tx ${l} p_l ) - if( p_l EQUAL -1 ) - message(WARNING "Language ${l} was present in previous translations and is now absent.") - set( tx_errors ON ) - endif() - endforeach() - +if ( curr_tx ) # New in list foreach( l ${curr_tx} ) - list( FIND prev_tx ${l} p_l ) - if( p_l EQUAL -1 ) - message(WARNING "Language ${l} is new.") - set( tx_errors ON ) - endif() set( p_l "lang/calamares_${l}.ts" ) if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${p_l} ) message(WARNING "Language ${l} has no .ts file yet.") @@ -463,7 +445,6 @@ if ( prev_tx ) unset( p_l ) unset( l ) endif() -unset( prev_tx ) unset( curr_tx ) if( tx_errors ) message( FATAL_ERROR "Translation warnings, see above." ) @@ -474,14 +455,6 @@ list( SORT CALAMARES_TRANSLATION_LANGUAGES ) add_subdirectory( lang ) # i18n tools -if ( INSTALL_COMPLETION ) - if( NOT CMAKE_INSTALL_BASHCOMPLETIONDIR ) - set( CMAKE_INSTALL_BASHCOMPLETIONDIR "${CMAKE_INSTALL_DATADIR}/bash-completion/completions" ) - endif() - - install( FILES ${CMAKE_SOURCE_DIR}/data/completion/bash/calamares DESTINATION "${CMAKE_INSTALL_BASHCOMPLETIONDIR}" ) -endif() - ### Example Distro # # For testing purposes Calamares includes a very, very, limited sample @@ -671,6 +644,14 @@ if( INSTALL_POLKIT ) ) endif() +if ( INSTALL_COMPLETION ) + if( NOT CMAKE_INSTALL_BASHCOMPLETIONDIR ) + set( CMAKE_INSTALL_BASHCOMPLETIONDIR "${CMAKE_INSTALL_DATADIR}/bash-completion/completions" ) + endif() + + install( FILES ${CMAKE_SOURCE_DIR}/data/completion/bash/calamares DESTINATION "${CMAKE_INSTALL_BASHCOMPLETIONDIR}" ) +endif() + install( FILES calamares.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications diff --git a/CMakeModules/CalamaresAddPlugin.cmake b/CMakeModules/CalamaresAddPlugin.cmake index 66536eda7..ee3c63acb 100644 --- a/CMakeModules/CalamaresAddPlugin.cmake +++ b/CMakeModules/CalamaresAddPlugin.cmake @@ -187,13 +187,22 @@ function( calamares_add_plugin ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_DESC_FILE} DESTINATION ${PLUGIN_DESTINATION} ) + set( _warned_config OFF ) foreach( PLUGIN_CONFIG_FILE ${PLUGIN_CONFIG_FILES} ) - configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) + if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} OR INSTALL_CONFIG ) + configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) + else() + message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" ) + set( _warned_config ON ) + endif() if ( INSTALL_CONFIG ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} DESTINATION ${PLUGIN_DATA_DESTINATION} ) endif() endforeach() + if ( _warned_config ) + message( "" ) + endif() endif() endfunction() diff --git a/CMakeModules/FindLibPWQuality.cmake b/CMakeModules/FindLibPWQuality.cmake index 84136f5ad..2728afb1e 100644 --- a/CMakeModules/FindLibPWQuality.cmake +++ b/CMakeModules/FindLibPWQuality.cmake @@ -6,10 +6,16 @@ # LibPWQuality_LIBRARIES, where to find the library # LibPWQuality_INCLUDE_DIRS, where to find pwquality.h # -include(FindPkgConfig) +find_package(PkgConfig) include(FindPackageHandleStandardArgs) -pkg_search_module(pc_pwquality QUIET pwquality) +if(PkgConfig_FOUND) + pkg_search_module(pc_pwquality QUIET pwquality) +else() + # It's just possible that the find_path and find_library will + # find it **anyway**, so let's pretend it was there. + set(pc_pwquality_FOUND ON) +endif() find_path(LibPWQuality_INCLUDE_DIR NAMES pwquality.h diff --git a/ci/RELEASE.sh b/ci/RELEASE.sh index 69aaec0be..82c77c3e2 100755 --- a/ci/RELEASE.sh +++ b/ci/RELEASE.sh @@ -78,7 +78,7 @@ if test "x$BUILD_DEFAULT" = "xtrue" ; then rm -rf "$BUILDDIR" mkdir "$BUILDDIR" || { echo "Could not create build directory." ; exit 1 ; } ( cd "$BUILDDIR" && cmake .. && make -j4 ) || { echo "Could not perform test-build in $BUILDDIR." ; exit 1 ; } - ( cd "$BUILDDIR" && make test ) || { echo "Tests failed in $BUILDDIR." ; exit 1 ; } + ( cd "$BUILDDIR" && make test ) || { echo "Tests failed in $BUILDDIR ." ; exit 1 ; } fi ### Build with clang @@ -95,7 +95,7 @@ if test "x$BUILD_CLANG" = "xtrue" ; then fi if test "x$BUILD_ONLY" = "xtrue" ; then - echo "Builds completed, release stopped. Build remains in $BUILDDIR." + echo "Builds completed, release stopped. Build remains in $BUILDDIR ." exit 1 fi @@ -106,20 +106,20 @@ else # Presumably -B was given; just do the cmake part rm -rf "$BUILDDIR" mkdir "$BUILDDIR" || { echo "Could not create build directory." ; exit 1 ; } - ( cd "$BUILDDIR" && cmake .. ) || { echo "Could not run cmake in $BUILDDIR." ; exit 1 ; } + ( cd "$BUILDDIR" && cmake .. ) || { echo "Could not run cmake in $BUILDDIR ." ; exit 1 ; } fi ### Get version number for this release # # V=$( cd "$BUILDDIR" && make show-version | grep ^CALAMARES_VERSION | sed s/^[A-Z_]*=// ) -test -n "$V" || { echo "Could not obtain version in $BUILDDIR." ; exit 1 ; } +test -n "$V" || { echo "Could not obtain version in $BUILDDIR ." ; exit 1 ; } ### Create signed tag # # This is the signing key ID associated with the GitHub account adriaandegroot, # which is used to create all "verified" tags in the Calamares repo. -KEY_ID="61A7D26277E4D0DB" +KEY_ID="CFDDC96F12B1915C" git tag -u "$KEY_ID" -m "Release v$V" "v$V" || { echo "Could not sign tag v$V." ; exit 1 ; } ### Create the tarball @@ -139,7 +139,7 @@ TMPDIR=$(mktemp -d --suffix="-calamares-$D") test -d "$TMPDIR" || { echo "Could not create tarball-build directory." ; exit 1 ; } tar xzf "$TAR_FILE" -C "$TMPDIR" || { echo "Could not unpack tarball." ; exit 1 ; } test -d "$TMPDIR/$TAR_V" || { echo "Tarball did not contain source directory." ; exit 1 ; } -( cd "$TMPDIR/$TAR_V" && cmake . && make -j4 && make test ) || { echo "Tarball build failed in $TMPDIR." ; exit 1 ; } +( cd "$TMPDIR/$TAR_V" && cmake . && make -j4 && make test ) || { echo "Tarball build failed in $TMPDIR ." ; exit 1 ; } ### Cleanup # diff --git a/ci/txcheck.sh b/ci/txcheck.sh index 9ce8f0c30..229cc8a73 100644 --- a/ci/txcheck.sh +++ b/ci/txcheck.sh @@ -29,7 +29,7 @@ ### END USAGE # The files that are translated; should match the contents of .tx/config -TX_FILE_LIST="lang/calamares_en.ts lang/python.pot src/modules/dummypythonqt/lang/dummypythonqt.pot calamares.desktop" +TX_FILE_LIST="lang/calamares_en.ts lang/python.pot calamares.desktop" ### COMMAND ARGUMENTS # @@ -125,7 +125,7 @@ tx_sum() # Remove linenumbers from .ts (XML) and .pot sed -i'' -e '/ calamares.desktop.new mv calamares.desktop.new calamares.desktop } diff --git a/ci/txstats.py b/ci/txstats.py index a9f412743..40fe3f43b 100755 --- a/ci/txstats.py +++ b/ci/txstats.py @@ -178,6 +178,7 @@ def get_tx_stats(languages, outputter, verbose): # and it's at-the-least ok. incomplete_languages = ( "eo", # Not supported by QLocale < 5.12.1 + "ie", # Not supported by Qt at least through 5.15.0 ) all_langs = [] diff --git a/lang/calamares_as.ts b/lang/calamares_as.ts index b087c0ba3..9f1da1ad3 100644 --- a/lang/calamares_as.ts +++ b/lang/calamares_as.ts @@ -212,17 +212,17 @@ Loading ... - + ভৰ্টিকৰন ... QML Step <i>%1</i>. - + QML Step <i>%1</i>. Loading failed. - + ভৰ্টিকৰন বিফল | @@ -717,7 +717,7 @@ The installer will quit and all changes will be lost. Set timezone to %1/%2. - + সময় অঞ্চলৰ সিদ্ধান্ত কৰক %`1%2 @@ -742,7 +742,7 @@ The installer will quit and all changes will be lost. Network Installation. (Disabled: internal error) - + নেটৱৰ্ক ইনস্তলেচন। (নিস্ক্ৰিয়: ভিতৰুৱা দোষ) @@ -777,22 +777,22 @@ The installer will quit and all changes will be lost. <h1>Welcome to the Calamares setup program for %1</h1> - + %1ৰ Calamares চেত্ আপ প্ৰগ্ৰামলৈ আদৰণি জনাইছো। <h1>Welcome to %1 setup</h1> - + <h1> %1 চেত্ আপলৈ আদৰণি জনাইছো।</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>%1ৰ কেলামাৰেচ ইনস্তলাৰলৈ আদৰণি জনাইছো।</h1> <h1>Welcome to the %1 installer</h1> - + <h1>%1 ইনস্তলাৰলৈ আদৰণি জনাইছো।</h1> @@ -802,7 +802,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as username. - + '%1'ক ব্যৱহাৰকাৰীৰ নাম হিচাপে ব্যৱহাৰ কৰা অবধ্য | @@ -827,7 +827,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as hostname. - + '%1'ক আয়োজকৰ নাম হিচাপে ব্যৱহাৰ কৰা অবধ্য | @@ -1768,7 +1768,7 @@ The installer will quit and all changes will be lost. Could not configure LUKS key file on partition %1. - + %1 বিভাজনত LUKS কি ফাইল কনফিগাৰ কৰিব পৰা নগ'ল। @@ -1796,7 +1796,9 @@ The installer will quit and all changes will be lost. Please select your preferred location on the map so the installer can suggest the locale and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming. - + অনুগ্ৰহ কৰি নিজৰ প্রিয় স্থান নক্সাখ্নত বাছক জাতে ইস্নস্তালাৰটোৱে অপোনাক থলী + আৰু সময় অঞ্চলৰ ছেটিংছ আপোৰ বাবে মিলাই দায়ক | আপোনি পৰামৰ্শমূলক ছেটিংছবোৰক তলত অনুকূলিত কৰিব পাৰে | নক্সাখ্নত পৈন্তেৰদালক টানি অনুসন্ধান কৰিব | + আৰু +/- বুটামেৰে যোম in/out কৰক বা মাউছৰ সচৰোলৰও ব্যবহাৰ কৰিব পাৰে | @@ -1810,92 +1812,92 @@ The installer will quit and all changes will be lost. Office software - + কাৰ্যালয়ৰ ছফটৱেৰ Office package - + কাৰ্যালয়ৰ পেকেজ Browser software - + ব্ৰাউজাৰৰ ছফটৱেৰ Browser package - + ব্ৰাউজাৰৰ পেকেজ Web browser - + ৱেব ব্ৰাউজাৰ Kernel - + কাৰ্ণেল Services - + সেৰ্ৱিচেস Login - + পৰীক্ষণ কৰক Desktop - + দেস্কেতোপ Applications - + এপ্লীকেছ্নচ Communication - + যোগাযোগ Development - + প্রবৃদ্ধি Office - + কাৰ্যালয় Multimedia - + মাল্টিমিডিয়া Internet - + ইণ্টাৰনেট Theming - + থিমীং Gaming - + খেলা Utilities - + সঁজুলি @@ -1903,7 +1905,7 @@ The installer will quit and all changes will be lost. Notes - + টোকা @@ -1942,12 +1944,12 @@ The installer will quit and all changes will be lost. Timezone: %1 - + সময় অঞ্চল: %1 To be able to select a timezone, make sure you are connected to the internet. Restart the installer after connecting. You can fine-tune Language and Locale settings below. - + সময় অঞ্চল বাছিবলৈ, ইণ্টাৰনেটত সংশ্লিষ্ট কৰি ৰাখিব | সংশ্লিষ্ট কৰি ইন্স্তালাৰটো পুনৰাৰম্ভ কৰক | আপোনি ভাসা অৰু থলীৰ ছেটিংছবোৰ তলত অনুকূলিত কৰিব পাৰে | @@ -2599,12 +2601,12 @@ The installer will quit and all changes will be lost. An EFI system partition is necessary to start %1.<br/><br/>To configure an EFI system partition, go back and select or create a FAT32 filesystem with the <strong>%3</strong> flag enabled and mount point <strong>%2</strong>.<br/><br/>You can continue without setting up an EFI system partition but your system may fail to start. - + %1 আৰম্ভ কৰিবলৈ এটা EFI চিছটেম থকাটো আৱশ্যক। <br/><br/>এটা EFI চিছটেম কন্ফিগাৰ কৰিবলৈ উভতি যাওক আৰু FAT32 ফাইল চিছটেম এটা বাচনি কৰক যিটোত <strong>%3</strong> ফ্লেগ সক্ষম হৈ আছে আৰু <strong>%2</strong> মাউন্ট্ পইণ্ট্ আছে।<br/><br/>আপুনি EFI চিছটেমবিভাজন কন্ফিগাৰ নকৰাকৈ অগ্ৰসৰ হ'ব পাৰে কিন্তু ইয়াৰ ফলত চিছটেম বিফল হ'ব পাৰে। An EFI system partition is necessary to start %1.<br/><br/>A partition was configured with mount point <strong>%2</strong> but its <strong>%3</strong> flag is not set.<br/>To set the flag, go back and edit the partition.<br/><br/>You can continue without setting the flag but your system may fail to start. - + %1 আৰম্ভ কৰিবলৈ এটা EFI চিছটেম থকাটো আৱশ্যক। %2 মাউন্ট্ পইন্ট্ নোহোৱকৈ কন্ফিগাৰ কৰা হৈছিল, কিন্তু ইয়াৰ esp ফ্লেগ ছেট কৰা হোৱা নাই। ফ্লেগ্ ছেট কৰিবলৈ উভতি যাওক আৰু বিভাজন সলনি কৰক। আপুনি ফ্লেগ ছেট নকৰাকৈ অগ্ৰসৰ হ'ব পাৰে কিন্তু ইয়াৰ ফলত চিছটেম বিফল হ'ব পাৰে। @@ -2614,12 +2616,12 @@ The installer will quit and all changes will be lost. Option to use GPT on BIOS - + GPTৰ BIOSত ব্যৱহাৰৰ বাবে বিকল্প A GPT partition table is the best option for all systems. This installer supports such a setup for BIOS systems too.<br/><br/>To configure a GPT partition table on BIOS, (if not done so already) go back and set the partition table to GPT, next create a 8 MB unformatted partition with the <strong>bios_grub</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. - + এটা GPT পৰ্তিসোন টেবুল সকলো স্যস্তেমৰ বাবে উত্তম বিকল্প হয় | এই ইন্সালাৰতোৱে তেনে স্থাপনকৰণ BIOS স্যস্তেমতো কৰে |<br/><br/>এটা GPT পৰ্তিসোন টেবুল কনফিগাৰ কৰিবলৈ ( যদি আগতে কৰা নাই ) পাছলৈ গৈ পৰ্তিসোন টেবুলখনক GPTলৈ পৰিৱৰ্তন কৰক, তাৰপাচত 8 MBৰ উনফোৰমেতেট পৰ্তিতিওন এটা বনাব | <strong>bios_grub</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. @@ -2639,7 +2641,7 @@ The installer will quit and all changes will be lost. There are no partitions to install on. - + ইনস্তল কৰিবলৈ কোনো বিভাজন নাই। @@ -2852,7 +2854,7 @@ Output: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + %1 চেত্ আপৰ বাবে পৰামৰ্শ দিয়া আৱশ্যকতা এই কম্পিউটাৰটোৱে পূৰ্ণ নকৰে। <br/>স্থাপন প্ৰক্ৰিয়া অবিৰত ৰাখিব পাৰিব, কিন্তু কিছুমান সুবিধা নিষ্ক্রিয় হৈ থাকিব। @@ -2963,13 +2965,14 @@ Output: <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> - + %1 ইনস্তলচেন​ৰ বাবে নিম্নতম আৱশ্যকতা এই কম্পিউটাৰটোৱে পূৰ্ণ নকৰে। + <br/>ইনস্তলচেন​ প্ৰক্ৰিয়া অবিৰত ৰাখিব নোৱাৰিব। <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + %1 চেত্ আপৰ বাবে পৰামৰ্শ দিয়া আৱশ্যকতা এই কম্পিউটাৰটোৱে পূৰ্ণ নকৰে। <br/>স্থাপন প্ৰক্ৰিয়া অবিৰত ৰাখিব পাৰিব, কিন্তু কিছুমান সুবিধা নিষ্ক্রিয় হৈ থাকিব। @@ -3435,28 +3438,28 @@ Output: KDE user feedback - + KDE ব্যৱহাৰকৰ্তাৰ সম্বন্ধীয় প্ৰতিক্ৰীয়া Configuring KDE user feedback. - + কনফিগাৰত KDE ব্যৱহাৰকৰ্তাৰ সম্বন্ধীয় প্ৰতিক্ৰীয়া Error in KDE user feedback configuration. - + KDE ব্যৱহাৰকৰ্তাৰ ফিডবেক কনফিগাৰেচনৰ ক্ৰুটি। Could not configure KDE user feedback correctly, script error %1. - + KDE ব্যৱহাৰকৰ্তাৰ প্ৰতিক্ৰিয়া ঠাকভাৱে কন্ফিগাৰ কৰিব পৰা নগ'ল, লিপি ক্ৰুটি %1। Could not configure KDE user feedback correctly, Calamares error %1. - + KDE ব্যৱহাৰকৰ্তাৰ প্ৰতিক্ৰিয়া ঠাকভাৱে কন্ফিগাৰ কৰিব পৰা নগ'ল, কেলামাৰেচ ক্ৰুটি %1। @@ -3503,7 +3506,7 @@ Output: <html><head/><body><p>Click here to send <span style=" font-weight:600;">no information at all</span> about your installation.</p></body></html> - + <html><head/><body><p>এইটো বাচনি কৰি, ইনস্তলচেন​ৰ বিষয়ে <span style=" font-weight:600;">মুঠতে একো তথ্য</span> আপুনি নপঠায়।</p></body></html> @@ -3518,17 +3521,17 @@ Output: By selecting this you will send information about your installation and hardware. This information will only be sent <b>once</b> after the installation finishes. - + এইটো বাচনি কৰি আপুনি ইনস্তলচেন​ আৰু হাৰ্ডৱেৰৰ বিষয়ে তথ্য পঠাব। ইনস্তলচেন​ৰ পিছত <b>এই তথ্য এবাৰ পঠোৱা হ'ব</b>। By selecting this you will periodically send information about your <b>machine</b> installation, hardware and applications, to %1. - + এইটো বাচনি কৰি আপুনি ইনস্তলচেন​, হাৰ্ডৱেৰ আৰু এপ্লিকেচনৰ বিষয়ে <b>মেচিন</b> %1লৈ তথ্য পঠাব। By selecting this you will regularly send information about your <b>user</b> installation, hardware, applications and application usage patterns, to %1. - + এইটো বাচনি কৰি আপুনি ইনস্তলচেন​, হাৰ্ডৱেৰ আৰু এপ্লিকেচনৰ বিষয়ে <b>ব্যৱহাৰকৰ্তা</b> %1লৈ তথ্য পঠাব। @@ -3727,7 +3730,7 @@ Output: <h1>%1</h1><br/><strong>%2<br/>for %3</strong><br/><br/>Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Copyright 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Thanks to <a href="https://calamares.io/team/">the Calamares team</a> and the <a href="https://www.transifex.com/calamares/calamares/">Calamares translators team</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> development is sponsored by <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software. - + <h1>%1</h1><br/><strong>%2<br/>ৰ বাবে %3</strong><br/><br/> মালিকিস্বত্ত 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>মালিকিস্বত্ত 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/><a href="https://calamares.io/team/">Calamares দল</a> আৰু <a href="https://www.transifex.com/calamares/calamares/">কেলামাৰেচ অনুবাদক দল</a>ক ধন্যবাদ জনাইছো।<br/><br/><a href="https://calamares.io/">Calamares</a>ৰ বিকাশ<br/><a href="http://www.blue-systems.com/">Blue Systems</a>- Liberating Softwareৰ দ্বাৰা প্ৰযোজিত। @@ -3762,12 +3765,23 @@ Output: development is sponsored by <br/> <a href='http://www.blue-systems.com/'>Blue Systems</a> - Liberating Software. - + <h1>%1</h1><br/> + <strong>%2<br/> + ৰ বাবে %3</strong><br/><br/> + মালিকিস্বত্ত 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/> + মালিকিস্বত্ত 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/> + ক ধন্যবাদ জনাইছো<a href='https://calamares.io/team/'> Calamares দল + আৰু <a href='https://www.transifex.com/calamares/calamares/'>Calamares + অনুবাদক দল</a>.<br/><br/> + <a href='https://calamares.io/'>Calamares</a> + ৰ বিকাশ<br/> + <a href='http://www.blue-systems.com/'>Blue Systems</a> - + Liberating Softwareৰ দ্বাৰা প্ৰযোজিত।. Back - + পাছলৈ @@ -3776,18 +3790,20 @@ Output: <h1>Languages</h1> </br> The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>. - + <h1>ভাষা</h1> </br> + চিছটেমৰ স্থানীয় ছেটিংস্ কমাণ্ডলাইনৰ কিছুমান উপভোক্তা ইন্টাৰফেছ উপাদানৰ ভাষা আৰু আখৰবোৰত প্ৰভাৱ পেলায়। বৰ্তমান ছেটিংস্ হ'ল: <strong>%1</strong>. <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>স্থানীয়</h1> </br> + চিছটেমৰ স্থানীয় ছেটিংসে উপাদানৰ নম্বৰ আৰু তাৰিখ সজ্জা প্ৰভাৱ পেলায়। বৰ্তমান ছেটিংস্ হ'ল: <strong>%1</strong>. Back - + পাছলৈ @@ -3795,44 +3811,44 @@ Output: Keyboard Model - + কিবোৰ্ড মডেল Pick your preferred keyboard model or use the default one based on the detected hardware - + আপোনাৰ প্ৰিয় কিবোৰ্ড মডেল চয়ন কৰক বা আপোনাৰ হাৰ্ডৱেৰৰ গতানুগতিকখ্নক ব্যৱহাৰ কৰক | Refresh - + সতেজ কৰক Layouts - + লেআউট Keyboard Layout - + কিবোৰ্ড লেআউট Models - + মডেল Variants - + ভিন্ন ৰুপ Test your keyboard - + কিবোৰ্ড পৰীক্ষা কৰক @@ -3840,7 +3856,7 @@ Output: Change - + সলনি @@ -3849,7 +3865,8 @@ Output: <h3>%1</h3> <p>These are example release notes.</p> - + <h3>%1</h3> + <p>এই খিনি ৰিলিজ নোতৰ উদাহৰণ </p> @@ -3882,7 +3899,7 @@ Output: Back - + পাছলৈ @@ -3891,32 +3908,33 @@ Output: <h3>Welcome to the %1 <quote>%2</quote> installer</h3> <p>This program will ask you some questions and set up %1 on your computer.</p> - + <h3>স্বাগতম আপোনাক %1 <quote>%2</quote> ইন্সালাৰটোত</h3> + <p>এই প্ৰোগ্ৰেমটোএয়ে অপোনাক কিৱছোমান প্ৰশ্ন সুধিব আৰু আপোনাৰ কোম্পিউটাৰত %1 স্থাপনকৰণ কৰিব |</p> About - + সম্পর্কে Support - + সহায় Known issues - + জ্ঞাত সমস্যা Release notes - + মুক্তি টোকা Donate - + দান কৰক diff --git a/lang/calamares_ast.ts b/lang/calamares_ast.ts index fb9402c5c..f10b353e9 100644 --- a/lang/calamares_ast.ts +++ b/lang/calamares_ast.ts @@ -776,22 +776,22 @@ L'instalador va colar y van perdese tolos cambeos. <h1>Welcome to the Calamares setup program for %1</h1> - + <h1>Afáyate nel programa de configuración de Calamares pa %1</h1> <h1>Welcome to %1 setup</h1> - + <h1>Afáyate na configuración de %1</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>Afáyate nel instalador Calamares pa %1</h1> <h1>Welcome to the %1 installer</h1> - + <h1>Afáyate nel instalador de %1</h1> @@ -1432,7 +1432,7 @@ L'instalador va colar y van perdese tolos cambeos. is running the installer as an administrator (root) - + ta executando l'instalador como alministrador (root) @@ -1447,7 +1447,7 @@ L'instalador va colar y van perdese tolos cambeos. has a screen large enough to show the whole installer - + tien una pantalla abondo grande como p'amosar tol instalador @@ -1884,7 +1884,7 @@ L'instalador va colar y van perdese tolos cambeos. Theming - + Estilu @@ -2851,7 +2851,8 @@ Salida: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Esti ordenador nun satisfaz nengún de los requirimientos aconseyaos pa configurar %1.<br/> + La configuración pue siguir pero quiciabes se desactiven dalgunes carauterístiques.</p> @@ -2962,13 +2963,15 @@ Salida: <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> - + <p>Esti ordenador nun satisfaz los requirimientos mínimos pa instalar %1.<br/> + La instalación nun pue siguir.</p> <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Esti ordenador nun satisfaz nengún de los requirimientos aconseyaos pa configurar %1.<br/> + La configuración pue siguir pero quiciabes se desactiven dalgunes carauterístiques.</p> @@ -3517,7 +3520,7 @@ Salida: By selecting this you will send information about your installation and hardware. This information will only be sent <b>once</b> after the installation finishes. - + Al esbillar esto vas unviar información tocante a la instalación y el hardware. Esta información va unviase namás <b>una vegada</b> dempués de finar la instalación. diff --git a/lang/calamares_az.ts b/lang/calamares_az.ts index 4656531e5..c762de2df 100644 --- a/lang/calamares_az.ts +++ b/lang/calamares_az.ts @@ -6,7 +6,7 @@ The <strong>boot environment</strong> of this system.<br><br>Older x86 systems only support <strong>BIOS</strong>.<br>Modern systems usually use <strong>EFI</strong>, but may also show up as BIOS if started in compatibility mode. - Bu sistemin <strong>açılış mühiti</strong>.<br><br>Köhnə x86 sistemlər yalnız <strong>BIOS</strong> dəstəkləyir.<br>Müasir sistemlər isə adətən <strong>EFI</strong> istifadə edir, lakin açılış mühiti əgər uyğun rejimdə başladılmışsa, həmçinin BİOS istiafadə edə bilər. + Sistemin <strong>açılış mühiti</strong>.<br><br>Köhnə x86 sistemlər yalnız <strong>BIOS</strong> dəstəkləyir.<br>Müasir sistemlər isə adətən <strong>EFI</strong> istifadə edir, lakin açılış mühiti əgər uyğun rejimdə başladılmışsa, həmçinin BİOS istiafadə edə bilər. @@ -65,12 +65,12 @@ GlobalStorage - Ümumi yaddaş + ÜmumiYaddaş JobQueue - Tapşırıq sırası + TapşırıqSırası @@ -530,7 +530,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + <strong>Əl ilə bölmək</strong><br/>Siz bölməni özünüz yarada və ölçüsünü dəyişə bilərsiniz. @@ -717,7 +717,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. Set timezone to %1/%2. - + Saat quraşağını təyin etmək %1/%2 @@ -802,7 +802,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as username. - + İstifadəçi adı '%1' ola bilməz @@ -827,7 +827,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as hostname. - + Host_adı '%1' ola bilməz @@ -3800,7 +3800,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Yerlər</h1></br> + Sistemin məkan ayarları say və tarix formatlarəna təsir edir. Cari ayar <strong>%1</strong>-dir diff --git a/lang/calamares_az_AZ.ts b/lang/calamares_az_AZ.ts index 9bc6e2aab..0c1077022 100644 --- a/lang/calamares_az_AZ.ts +++ b/lang/calamares_az_AZ.ts @@ -530,7 +530,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + <strong>Əl ilə bölmək</strong><br/>Siz bölməni özünüz yarada və ölçüsünü dəyişə bilərsiniz. @@ -717,7 +717,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. Set timezone to %1/%2. - + Saat quraşağını təyin etmək %1/%2 @@ -802,7 +802,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as username. - + İstifadəçi adı '%1' ola bilməz @@ -827,7 +827,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as hostname. - + Host_adı '%1' ola bilməz @@ -3800,7 +3800,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Yerlər</h1></br> + Sistemin məkan ayarları say və tarix formatlarəna təsir edir. Cari ayar <strong>%1</strong>-dir diff --git a/lang/calamares_bn.ts b/lang/calamares_bn.ts index aec314a22..1c279ae56 100644 --- a/lang/calamares_bn.ts +++ b/lang/calamares_bn.ts @@ -6,17 +6,17 @@ The <strong>boot environment</strong> of this system.<br><br>Older x86 systems only support <strong>BIOS</strong>.<br>Modern systems usually use <strong>EFI</strong>, but may also show up as BIOS if started in compatibility mode. - + এই সিস্টেমের <strong>বুট পরিবেশ</strong>।<br><br> পুরাতন x86 সিস্টেম শুধুমাত্র <strong>BIOS</strong> সমর্থন কর<br> আধুনিক সিস্টেম সাধারণত <strong>EFI</strong> ব্যবহার করে, কিন্তু যদি সামঞ্জস্যতা মোডে শুরু হয় তাহলে BIOS হিসেবেও প্রদর্শিত হতে পারে। This system was started with an <strong>EFI</strong> boot environment.<br><br>To configure startup from an EFI environment, this installer must deploy a boot loader application, like <strong>GRUB</strong> or <strong>systemd-boot</strong> on an <strong>EFI System Partition</strong>. This is automatic, unless you choose manual partitioning, in which case you must choose it or create it on your own. - + এই সিস্টেম একটি <strong>EFI</strong> বুট পরিবেশ দিয়ে শুরু হয়েছিল।<br><br> একটি EFI পরিবেশ থেকে স্টার্টআপ কনফিগার করতে, এই ইনস্টলার অবশ্যই একটি <strong>EFI সিস্টেম পার্টিশনে</strong> <strong>GRUB</strong> বা <strong>systemd-boot </strong> এর মত একটি বুট লোডার অ্যাপ্লিকেশন প্রয়োগ করতে হবে। এটি স্বয়ংক্রিয়, যদি না আপনি ম্যানুয়াল পার্টিশনিং নির্বাচন করেন, সেক্ষেত্রে আপনাকে অবশ্যই এটি বেছে নিতে হবে অথবা এটি নিজে তৈরি করতে হবে। This system was started with a <strong>BIOS</strong> boot environment.<br><br>To configure startup from a BIOS environment, this installer must install a boot loader, like <strong>GRUB</strong>, either at the beginning of a partition or on the <strong>Master Boot Record</strong> near the beginning of the partition table (preferred). This is automatic, unless you choose manual partitioning, in which case you must set it up on your own. - + এই সিস্টেম একটি <strong>BIOS</strong> বুট পরিবেশ দিয়ে শুরু হয়েছিল।<br><br>একটি BIOS পরিবেশ থেকে স্টার্টআপ কনফিগার করতে, এই ইনস্টলার অবশ্যই GRUB এর মত একটি পার্টিশনের শুরুতে অথবা পার্টিশন টেবিলের শুরুতে মাস্টার বুট রেকর্ডে (পছন্দনীয়) মত একটি বুট লোডার ইনস্টল করতে হবে। এটি স্বয়ংক্রিয়, যদি না আপনি ম্যানুয়াল পার্টিশনিং নির্বাচন করেন, সেক্ষেত্রে আপনাকে অবশ্যই এটি নিজেই সেট আপ করতে হবে। @@ -39,12 +39,12 @@ Do not install a boot loader - + একটি বুট লোডার ইনস্টল করবেন না %1 (%2) - + %1 (%2) @@ -60,22 +60,22 @@ Form - + ফর্ম GlobalStorage - + গ্লোবাল স্টোরেজ JobQueue - + জব লাইন Modules - + মডিউলগুলো @@ -111,7 +111,7 @@ Debug information - + তথ্য ডিবাগ করুন @@ -124,7 +124,7 @@ Install - + ইনস্টল করুন @@ -171,7 +171,7 @@ Running command %1 %2 - + কমান্ড %1 %2 চলছে @@ -179,22 +179,22 @@ Running %1 operation. - + %1 ক্রিয়াকলাপ চলছে। Bad working directory path - খারাপ ওয়ার্কিং ডিরেক্টরি পাথ + ওয়ার্কিং ডিরেক্টরি পাথ ভালো নয় Working directory %1 for python job %2 is not readable. - ওয়ার্কিং ডিরেক্টরি 1% পাইথন কাজের জন্য % 2 পাঠযোগ্য নয়। + ওয়ার্কিং ডিরেক্টরি 1% পাইথন কাজের জন্য %2 পাঠযোগ্য নয়। Bad main script file - খারাপ প্রধান স্ক্রিপ্ট ফাইল + প্রধান স্ক্রিপ্ট ফাইল ভালো নয় @@ -264,7 +264,7 @@ Installation Failed - ইনস্টলেশন ব্যর্থ হয়েছে + ইনস্টলেশন ব্যর্থ হলো @@ -321,7 +321,7 @@ Continue with setup? - + সেটআপ চালিয়ে যেতে চান? @@ -336,7 +336,7 @@ The %1 installer is about to make changes to your disk in order to install %2.<br/><strong>You will not be able to undo these changes.</strong> - + %1 ইনস্টলার %2 সংস্থাপন করতে আপনার ডিস্কে পরিবর্তন করতে যাচ্ছে। @@ -346,12 +346,12 @@ &Install now - + এবংএখনই ইনস্টল করুন Go &back - + এবংফিরে যান @@ -401,7 +401,7 @@ &Cancel - + এবংবাতিল করুন @@ -411,7 +411,7 @@ Cancel installation? - + ইনস্টলেশন বাতিল করবেন? @@ -423,7 +423,8 @@ The setup program will quit and all changes will be lost. Do you really want to cancel the current install process? The installer will quit and all changes will be lost. - + আপনি কি সত্যিই বর্তমান সংস্থাপন প্রক্রিয়া বাতিল করতে চান? +ইনস্টলার টি বন্ধ হয়ে যাবে এবং সকল পরিবর্তন হারিয়ে যাবে। @@ -431,22 +432,22 @@ The installer will quit and all changes will be lost. Unknown exception type - অজানা ব্যতিক্রম প্রকার + অজানা ধরনের ব্যতিক্রম unparseable Python error - আনপারসেবল পাইথন ত্রুটি + অতুলনীয় পাইথন ত্রুটি unparseable Python traceback - আনপারসেবল পাইথন ট্রেসব্যাক + অতুলনীয় পাইথন ট্রেসব্যাক Unfetchable Python error. - অপরিবর্তনীয় পাইথন ত্রুটি। + অতুলনীয় পাইথন ত্রুটি। @@ -463,7 +464,7 @@ The installer will quit and all changes will be lost. Show debug information - + ডিবাগ তথ্য দেখান @@ -478,7 +479,7 @@ The installer will quit and all changes will be lost. &Cancel - + এবংবাতিল করুন @@ -496,7 +497,7 @@ The installer will quit and all changes will be lost. Gathering system information... - + সিস্টেম তথ্য সংগ্রহ করা হচ্ছে... @@ -504,12 +505,12 @@ The installer will quit and all changes will be lost. Form - + ফর্ম Select storage de&vice: - + স্টোরেজ ডিএবংভাইস নির্বাচন করুন: @@ -517,12 +518,12 @@ The installer will quit and all changes will be lost. Current: - + বর্তমান: After: - + পরে: @@ -537,7 +538,7 @@ The installer will quit and all changes will be lost. <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> - + <strong>সংকুচিত করার জন্য একটি পার্টিশন নির্বাচন করুন, তারপর নিচের বারটি পুনঃআকারের জন্য টেনে আনুন</strong> @@ -547,12 +548,12 @@ The installer will quit and all changes will be lost. Boot loader location: - + বুট লোডার অবস্থান: <strong>Select a partition to install on</strong> - + <strong>ইনস্টল করতে একটি পার্টিশন নির্বাচন করুন</strong> @@ -562,17 +563,17 @@ The installer will quit and all changes will be lost. The EFI system partition at %1 will be used for starting %2. - + %1 এ EFI সিস্টেম পার্টিশন %2 শুরু করার জন্য ব্যবহার করা হবে। EFI system partition: - + EFI সিস্টেম পার্টিশন: This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই স্টোরেজ ডিভাইসে কোন অপারেটিং সিস্টেম আছে বলে মনে হয় না। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন। @@ -580,7 +581,7 @@ The installer will quit and all changes will be lost. <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. - + <strong>ডিস্ক মুছে ফেলুন</strong> <br/>এটি বর্তমানে নির্বাচিত স্টোরেজ ডিভাইসে উপস্থিত সকল উপাত্ত <font color="red">মুছে ফেলবে</font>। @@ -588,7 +589,7 @@ The installer will quit and all changes will be lost. <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. - + <strong>ইনস্টল করুন পাশাপাশি</strong> <br/>ইনস্টলার %1 এর জন্য জায়গা তৈরি করতে একটি পার্টিশন সংকুচিত করবে। @@ -596,22 +597,22 @@ The installer will quit and all changes will be lost. <strong>Replace a partition</strong><br/>Replaces a partition with %1. - + <strong>একটি পার্টিশন প্রতিস্থাপন করুন</strong><br/>%1-এর সাথে একটি পার্টিশন প্রতিস্থাপন করে। This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই সঞ্চয় যন্ত্রটিতে %1 আছে। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন। This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই স্টোরেজ ডিভাইসে ইতোমধ্যে একটি অপারেটিং সিস্টেম আছে। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন. This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই স্টোরেজ ডিভাইসে একাধিক অপারেটিং সিস্টেম আছে। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন. @@ -644,17 +645,17 @@ The installer will quit and all changes will be lost. Clear mounts for partitioning operations on %1 - + %1 এ পার্টিশনিং অপারেশনের জন্য মাউন্ট গুলি মুছে ফেলুন Clearing mounts for partitioning operations on %1. - + %1-এ পার্টিশনিং অপারেশনের জন্য মাউন্ট মুছে ফেলা হচ্ছে। Cleared all mounts for %1 - + %1-এর জন্য সকল মাউন্ট মুছে ফেলা হয়েছে @@ -662,22 +663,22 @@ The installer will quit and all changes will be lost. Clear all temporary mounts. - + সব অস্থায়ী মাউন্ট পরিষ্কার করুন। Clearing all temporary mounts. - + সব অস্থায়ী মাউন্ট পরিষ্কার করা হচ্ছে। Cannot get list of temporary mounts. - + অস্থায়ী মাউন্টের তালিকা পাওয়া যাচ্ছে না। Cleared all temporary mounts. - + সব অস্থায়ী মাউন্ট পরিষ্কার করা হয়েছে. @@ -704,12 +705,12 @@ The installer will quit and all changes will be lost. Set keyboard model to %1.<br/> - + %1-এ কীবোর্ড নকশা নির্ধারণ করুন। Set keyboard layout to %1/%2. - + %1/%2 এ কীবোর্ড বিন্যাস নির্ধারণ করুন। @@ -850,7 +851,7 @@ The installer will quit and all changes will be lost. Si&ze: - + আএবংকার @@ -860,22 +861,22 @@ The installer will quit and all changes will be lost. Partition &Type: - পার্টিশন এবং প্রকার: + পার্টিশন এবংধরন: &Primary - এবং প্রাথমিক + এবংপ্রাথমিক E&xtended - সম্প্রসারিত + বএবংর্ধিত Fi&le System: - + ফাএবংইল সিস্টেম: @@ -885,7 +886,7 @@ The installer will quit and all changes will be lost. &Mount Point: - এবং মাউন্ট পয়েন্ট: + এবংমাউন্ট পয়েন্ট: @@ -900,17 +901,17 @@ The installer will quit and all changes will be lost. Logical - + যৌক্তিক Primary - + প্রাথমিক GPT - + জিপিটি @@ -933,12 +934,12 @@ The installer will quit and all changes will be lost. Creating new %1 partition on %2. - + %2-এ নতুন %1 পার্টিশন তৈরি করা হচ্ছে। The installer failed to create partition on disk '%1'. - + ইনস্টলার '%1' ডিস্কে পার্টিশন তৈরি করতে ব্যর্থ হয়েছে। @@ -946,27 +947,27 @@ The installer will quit and all changes will be lost. Create Partition Table - + পার্টিশন টেবিল তৈরি করুন Creating a new partition table will delete all existing data on the disk. - + একটি নতুন পার্টিশন টেবিল তৈরি করলে ডিস্কের সকল বিদ্যমান উপাত্ত মুছে যাবে। What kind of partition table do you want to create? - + আপনি কি ধরনের পার্টিশন টেবিল তৈরি করতে চান? Master Boot Record (MBR) - + মাস্টার বুট রেকর্ড (এমবিআর) GUID Partition Table (GPT) - + জিইউআইডি পার্টিশন টেবিল (জিপিটি) @@ -974,22 +975,22 @@ The installer will quit and all changes will be lost. Create new %1 partition table on %2. - + %2-এ নতুন %1 পার্টিশন টেবিল তৈরি করুন। Create new <strong>%1</strong> partition table on <strong>%2</strong> (%3). - + <strong>%2</strong> (%3) এ নতুন <strong>%1</strong> পার্টিশন টেবিল তৈরি করুন। Creating new %1 partition table on %2. - + %2 এ নতুন %1 পার্টিশন টেবিল তৈরি করা হচ্ছে। The installer failed to create a partition table on %1. - + ইনস্টলার %1 এ একটি পার্টিশন টেবিল তৈরি করতে ব্যর্থ হয়েছে। @@ -997,27 +998,27 @@ The installer will quit and all changes will be lost. Create user %1 - + %1 ব্যবহারকারী তৈরি করুন Create user <strong>%1</strong>. - + ব্যবহারকারী %1 তৈরি করুন। Creating user %1. - + ব্যবহারকারী %1 তৈরি করা হচ্ছে। Cannot create sudoers file for writing. - + লেখার জন্য sudoers ফাইল তৈরি করা যাবে না। Cannot chmod sudoers file. - + Sudoers ফাইল chmod করা যাবে না। @@ -1075,22 +1076,22 @@ The installer will quit and all changes will be lost. Delete partition %1. - + পার্টিশন %1 মুছে ফেলুন। Delete partition <strong>%1</strong>. - + পার্টিশন <strong>%1</strong> মুছে ফেলুন। Deleting partition %1. - + পার্টিশন %1 মুছে ফেলা হচ্ছে। The installer failed to delete partition %1. - + ইনস্টলার %1 পার্টিশন মুছে ফেলতে ব্যর্থ হয়েছে। @@ -1098,22 +1099,22 @@ The installer will quit and all changes will be lost. This device has a <strong>%1</strong> partition table. - + এই যন্ত্রটির একটি <strong>%1</strong> পার্টিশন টেবিল আছে। This is a <strong>loop</strong> device.<br><br>It is a pseudo-device with no partition table that makes a file accessible as a block device. This kind of setup usually only contains a single filesystem. - + এটি একটি <strong>লুপ</strong> ডিভাইস।<br><br>এটি একটি ছদ্ম-ডিভাইস যার কোন পার্টিশন টেবিল নেই যা একটি ফাইলকে ব্লক ডিভাইস হিসেবে অ্যাক্সেসযোগ্য করে তোলে। এই ধরনের উপস্থাপনা সাধারণত শুধুমাত্র একটি একক ফাইলসিস্টেম ধারণ করে। This installer <strong>cannot detect a partition table</strong> on the selected storage device.<br><br>The device either has no partition table, or the partition table is corrupted or of an unknown type.<br>This installer can create a new partition table for you, either automatically, or through the manual partitioning page. - + এই ইনস্টলার নির্বাচিত সঞ্চয় ডিভাইসে <strong>একটি পার্টিশন টেবিল শনাক্ত করতে পারে না</strong>।<br> <br>ডিভাইসটির কোন পার্টিশন টেবিল নেই, অথবা পার্টিশন টেবিলটি দূষিত অথবা একটি অজানা ধরনের।<br> এই ইনস্টলার আপনার জন্য একটি নতুন পার্টিশন টেবিল তৈরি করতে পারে, হয় স্বয়ংক্রিয়ভাবে, অথবা ম্যানুয়াল পার্টিশনিং পৃষ্ঠার মাধ্যমে। <br><br>This is the recommended partition table type for modern systems which start from an <strong>EFI</strong> boot environment. - + <br><br>এটি আধুনিক সিস্টেমের জন্য প্রস্তাবিত পার্টিশন টেবিলের ধরন যা একটি <strong>EFI</strong> বুট পরিবেশ থেকে শুরু হয়। @@ -1123,7 +1124,7 @@ The installer will quit and all changes will be lost. The type of <strong>partition table</strong> on the selected storage device.<br><br>The only way to change the partition table type is to erase and recreate the partition table from scratch, which destroys all data on the storage device.<br>This installer will keep the current partition table unless you explicitly choose otherwise.<br>If unsure, on modern systems GPT is preferred. - + নির্বাচিত স্টোরেজ ডিভাইসে <strong>পার্টিশন টেবিলে</strong>র ধরন। <br><br>পার্টিশন টেবিলের ধরন পরিবর্তনের একমাত্র উপায় হল স্ক্র্যাচ থেকে পার্টিশন টেবিল মুছে ফেলা এবং পুনরায় তৈরি করা, যা স্টোরেজ ডিভাইসের সমস্ত ডাটা ধ্বংস করে।<br> এই ইনস্টলার বর্তমান পার্টিশন টেবিল রাখবে যদি না আপনি স্পষ্টভাবে অন্যভাবে নির্বাচন করেন. যদি অনিশ্চিত থাকেন, আধুনিক সিস্টেমে জিপিটি অগ্রাধিকার দেওয়া হয়। @@ -1132,7 +1133,7 @@ The installer will quit and all changes will be lost. %1 - %2 (%3) device[name] - size[number] (device-node[name]) - + %1 - %2 (%3) @@ -1172,27 +1173,27 @@ The installer will quit and all changes will be lost. Edit Existing Partition - + বিদ্যমান পার্টিশন সম্পাদনা করুন Content: - + বিষয়বস্তু: &Keep - + এবংরাখুন Format - + বিন্যাস Warning: Formatting the partition will erase all existing data. - + সতর্কীকরণ: পার্টিশন ফরম্যাট করলে সমস্ত বিদ্যমান উপাত্ত মুছে ফেলবে। @@ -1202,7 +1203,7 @@ The installer will quit and all changes will be lost. Si&ze: - + আএবংকার @@ -1212,7 +1213,7 @@ The installer will quit and all changes will be lost. Fi&le System: - + ফাএবংইল সিস্টেম: @@ -1230,7 +1231,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -1258,37 +1259,37 @@ The installer will quit and all changes will be lost. Set partition information - + পার্টিশন তথ্য নির্ধারণ করুন Install %1 on <strong>new</strong> %2 system partition. - + <strong>নতুন</strong> %2 সিস্টেম পার্টিশনে %1 সংস্থাপন করুন। Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong>. - + মাউন্ট বিন্দু <strong>%1</strong> দিয়ে <strong>নতুন</strong> %2 পার্টিশন বিন্যাস করুন। Install %2 on %3 system partition <strong>%1</strong>. - + %3 সিস্টেম পার্টিশন <strong>%1</strong> এ %2 ইনস্টল করুন। Set up %3 partition <strong>%1</strong> with mount point <strong>%2</strong>. - + %3 পার্টিশন <strong>%1</strong> মাউন্ট বিন্দু <strong>%2</strong> দিয়ে বিন্যাস করুন। Install boot loader on <strong>%1</strong>. - + <strong>%1</strong> এ বুট লোডার ইনস্টল করুন। Setting up mount points. - + মাউন্ট পয়েন্ট সেট আপ করা হচ্ছে। @@ -1296,12 +1297,12 @@ The installer will quit and all changes will be lost. Form - + ফর্ম &Restart now - + এবংএখন আবার চালু করুন @@ -1316,7 +1317,7 @@ The installer will quit and all changes will be lost. <h1>All done.</h1><br/>%1 has been installed on your computer.<br/>You may now restart into your new system, or continue using the %2 Live environment. - + <h1>সব শেষ।</h1><br/>%1 আপনার কম্পিউটারে সংস্থাপন করা হয়েছে।<br/>আপনি এখন আপনার নতুন সিস্টেমে পুনর্সূচনা করতে পারেন, অথবা %2 লাইভ পরিবেশ ব্যবহার চালিয়ে যেতে পারেন। @@ -1339,7 +1340,7 @@ The installer will quit and all changes will be lost. Finish - + শেষ করুন @@ -1377,12 +1378,12 @@ The installer will quit and all changes will be lost. Formatting partition %1 with file system %2. - + ফাইল সিস্টেম %2 দিয়ে পার্টিশন %1 বিন্যাস করা হচ্ছে। The installer failed to format partition %1 on disk '%2'. - + ইনস্টলার '%2' ডিস্কে %1 পার্টিশন বিন্যাস করতে ব্যর্থ হয়েছে। @@ -1523,7 +1524,7 @@ The installer will quit and all changes will be lost. Executing script: &nbsp;<code>%1</code> - + স্ক্রিপ্ট কার্যকর করা হচ্ছে: &nbsp;<code>%1</code> @@ -1531,7 +1532,7 @@ The installer will quit and all changes will be lost. Script - + স্ক্রিপ্ট @@ -1539,12 +1540,12 @@ The installer will quit and all changes will be lost. Set keyboard model to %1.<br/> - + %1 এ কীবোর্ড নকশা নির্ধারণ করুন। Set keyboard layout to %1/%2. - + %1/%2 এ কীবোর্ড বিন্যাস নির্ধারণ করুন। @@ -1552,7 +1553,7 @@ The installer will quit and all changes will be lost. Keyboard - + কীবোর্ড @@ -1560,7 +1561,7 @@ The installer will quit and all changes will be lost. Keyboard - + কীবোর্ড @@ -1568,17 +1569,17 @@ The installer will quit and all changes will be lost. System locale setting - + সিস্টেম লোকেল সেটিং The system locale setting affects the language and character set for some command line user interface elements.<br/>The current setting is <strong>%1</strong>. - + সিস্টেম স্থানীয় বিন্যাসন কিছু কমান্ড লাইন ব্যবহারকারী ইন্টারফেস উপাদানের জন্য ভাষা এবং অক্ষর সেট কে প্রভাবিত করে. <br/>বর্তমান বিন্যাসন <strong>%1</strong>। &Cancel - + এবংবাতিল করুন @@ -1591,7 +1592,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -1601,7 +1602,7 @@ The installer will quit and all changes will be lost. I accept the terms and conditions above. - + আমি উপরের শর্তাবলী মেনে নিচ্ছি। @@ -1634,7 +1635,7 @@ The installer will quit and all changes will be lost. License - + লাইসেন্স @@ -1702,18 +1703,18 @@ The installer will quit and all changes will be lost. Region: - + অঞ্চল: Zone: - + বলয়: &Change... - + এবংপরিবর্তন করুন... @@ -1721,7 +1722,7 @@ The installer will quit and all changes will be lost. Location - + অবস্থান @@ -1729,7 +1730,7 @@ The installer will quit and all changes will be lost. Location - + অবস্থান @@ -2200,7 +2201,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -2241,7 +2242,7 @@ The installer will quit and all changes will be lost. Name - + নাম @@ -2254,17 +2255,17 @@ The installer will quit and all changes will be lost. Form - + ফর্ম Keyboard Model: - + কীবোর্ড নকশা: Type here to test your keyboard - + আপনার কীবোর্ড পরীক্ষা করতে এখানে টাইপ করুন @@ -2272,12 +2273,12 @@ The installer will quit and all changes will be lost. Form - + ফর্ম What is your name? - + আপনার নাম কি? @@ -2287,7 +2288,7 @@ The installer will quit and all changes will be lost. What name do you want to use to log in? - + লগ-ইন করতে আপনি কোন নাম ব্যবহার করতে চান? @@ -2297,12 +2298,12 @@ The installer will quit and all changes will be lost. What is the name of this computer? - + এই কম্পিউটারের নাম কি? <small>This name will be used if you make the computer visible to others on a network.</small> - + <small>আপনি যদি কম্পিউটারটিকে অন্যদের কাছে একটি নেটওয়ার্কে দৃশ্যমান করেন তাহলে এই নামটি ব্যবহার করা হবে।</small> @@ -2312,13 +2313,13 @@ The installer will quit and all changes will be lost. Choose a password to keep your account safe. - + আপনার অ্যাকাউন্ট সুরক্ষিত রাখতে একটি পাসওয়ার্ড নির্বাচন করুন। <small>Enter the same password twice, so that it can be checked for typing errors. A good password will contain a mixture of letters, numbers and punctuation, should be at least eight characters long, and should be changed at regular intervals.</small> - + <small>একই পাসওয়ার্ড দুইবার প্রবেশ করান, যাতে এটি টাইপিং ত্রুটির জন্য পরীক্ষা করা যেতে পারে। একটি ভাল পাসওয়ার্ড অক্ষর, সংখ্যা এবং বিরামচিহ্নের একটি মিশ্রণ ধারণ করবে, অন্তত আট অক্ষর দীর্ঘ হওয়া উচিত, এবং নিয়মিত বিরতিতে পরিবর্তন করা উচিত।</small> @@ -2345,23 +2346,23 @@ The installer will quit and all changes will be lost. Log in automatically without asking for the password. - + পাসওয়ার্ড না চাইলে স্বয়ংক্রিয়ভাবে লগ ইন করুন। Use the same password for the administrator account. - + প্রশাসক হিসাবের জন্য একই গুপ্ত-সংকেত ব্যবহার করুন। Choose a password for the administrator account. - + প্রশাসক হিসাবের জন্য একটি পাসওয়ার্ড নির্বাচন করুন। <small>Enter the same password twice, so that it can be checked for typing errors.</small> - + <small>একই পাসওয়ার্ড দুইবার প্রবেশ করান, যাতে এটি টাইপিং ত্রুটির জন্য পরীক্ষা করা যেতে পারে।</small> @@ -2369,43 +2370,43 @@ The installer will quit and all changes will be lost. Root - + রুট Home - + বাড়ি Boot - + বুট EFI system - + ইএফআই সিস্টেম Swap - + অদলবদল New partition for %1 - + %1 এর জন্য নতুন পার্টিশন New partition - + নতুন পার্টিশন %1 %2 size[number] filesystem[name] - + %1 %2 @@ -2414,33 +2415,33 @@ The installer will quit and all changes will be lost. Free Space - + খালি জায়গা New partition - + নতুন পার্টিশন Name - + নাম File System - + নথি ব্যবস্থা Mount Point - + মাউন্ট পয়েন্ট Size - + আকার @@ -2448,22 +2449,22 @@ The installer will quit and all changes will be lost. Form - + ফর্ম Storage de&vice: - + স্টোরেজ ডিএবংভাইস &Revert All Changes - + এবংসকল পরিবর্তন ফিরিয়ে দিন New Partition &Table - + নতুন পার্টিশন এবংটেবিল @@ -2473,12 +2474,12 @@ The installer will quit and all changes will be lost. &Edit - + এবংসম্পাদনা করুন &Delete - + এবংমুছে ফেলুন @@ -2508,7 +2509,7 @@ The installer will quit and all changes will be lost. Are you sure you want to create a new partition table on %1? - + আপনি কি নিশ্চিত যে আপনি %1 এ একটি নতুন পার্টিশন টেবিল তৈরি করতে চান? @@ -2526,67 +2527,67 @@ The installer will quit and all changes will be lost. Gathering system information... - + সিস্টেম তথ্য সংগ্রহ করা হচ্ছে... Partitions - + পার্টিশনগুলো Install %1 <strong>alongside</strong> another operating system. - + অন্য অপারেটিং সিস্টেমের <strong>পাশাপাশি</strong> %1 ইনস্টল করুন। <strong>Erase</strong> disk and install %1. - + ডিস্ক <strong>মুছে ফেলুন</strong> এবং %1 সংস্থাপন করুন। <strong>Replace</strong> a partition with %1. - + %1 দিয়ে একটি পার্টিশন <strong>প্রতিস্থাপন করুন</strong>। <strong>Manual</strong> partitioning. - + <strong>ম্যানুয়াল</strong> পার্টিশনিং। Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). - + <strong>%2</strong> (%3) ডিস্কে অন্য অপারেটিং সিস্টেমের <strong>পাশাপাশি</strong> %1 ইনস্টল করুন। <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. - + ডিস্ক <strong>%2</strong> (%3) <strong>মুছে ফেলুন</strong> এবং %1 সংস্থাপন করুন। <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. - + %1 দিয়ে <strong>%2</strong> (%3) ডিস্কে একটি পার্টিশন <strong>প্রতিস্থাপন করুন</strong>। <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). - + <strong>%1</strong> (%2) ডিস্কে <strong>ম্যানুয়াল</strong> পার্টিশন করা হচ্ছে। Disk <strong>%1</strong> (%2) - + ডিস্ক <strong>%1</strong> (%2) Current: - + বর্তমান: After: - + পরে: @@ -2658,7 +2659,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -2768,22 +2769,22 @@ Output: %1 (%2) - + %1 (%2) unknown - + অজানা extended - + বর্ধিত unformatted - + অবিন্যাসিত @@ -2793,13 +2794,13 @@ Output: Default Keyboard Model - + পূর্বনির্ধারিত কীবোর্ডের নকশা Default - + পূর্বনির্ধারিত @@ -2837,7 +2838,7 @@ Output: Unpartitioned space or unknown partition table - + অবিভাজনকৃত স্থান বা অজানা পার্টিশন টেবিল @@ -2881,74 +2882,74 @@ Output: Form - + ফর্ম Select where to install %1.<br/><font color="red">Warning: </font>this will delete all files on the selected partition. - + %1 কোথায় সংস্থাপন করতে হবে তা নির্বাচন করুন।<br/><font color="red"> সতর্কীকরণ: </font>এটি নির্বাচিত পার্টিশনের সকল ফাইল মুছে ফেলবে। The selected item does not appear to be a valid partition. - + নির্বাচিত বিষয়োপকরণটি একটি বৈধ পার্টিশন বলে মনে হচ্ছে না। %1 cannot be installed on empty space. Please select an existing partition. - + %1 ফাঁকা স্থানে সংস্থাপন করা যাবে না। অনুগ্রহ করে একটি বিদ্যমান পার্টিশন নির্বাচন করুন। %1 cannot be installed on an extended partition. Please select an existing primary or logical partition. - + %1 একটি বর্ধিত পার্টিশনে সংস্থাপন করা যাবে না। অনুগ্রহ করে একটি বিদ্যমান প্রাথমিক বা যৌক্তিক বিভাজন নির্বাচন করুন। %1 cannot be installed on this partition. - + %1 এই পার্টিশনে সংস্থাপন করা যাবে না। Data partition (%1) - + ডাটা পার্টিশন (%1) Unknown system partition (%1) - + অজানা সিস্টেম পার্টিশন (%1) %1 system partition (%2) - + %1 সিস্টেম পার্টিশন (%2) <strong>%4</strong><br/><br/>The partition %1 is too small for %2. Please select a partition with capacity at least %3 GiB. - + <strong>%4</strong><br/><br/>%1 পার্টিশন %2 এর জন্য খুবই ছোট। অনুগ্রহ করে কমপক্ষে %3 GiB ধারণ ক্ষমতা সম্পন্ন একটি পার্টিশন নির্বাচন করুন। <strong>%2</strong><br/><br/>An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. - + <strong>%2</strong><br/><br/> একটি EFI সিস্টেম পার্টিশন এই সিস্টেমের কোথাও খুঁজে পাওয়া যাবে না। অনুগ্রহ করে ফিরে যান এবং %1 সেট আপ করতে ম্যানুয়াল পার্টিশনিং ব্যবহার করুন। <strong>%3</strong><br/><br/>%1 will be installed on %2.<br/><font color="red">Warning: </font>all data on partition %2 will be lost. - + <strong>%3</strong><br/><br/>%1 %2-এ ইনস্টল করা হবে।<br/><font color="red"> সতর্কীকরণ: </font>%2 পার্টিশনের সকল উপাত্ত হারিয়ে যাবে। The EFI system partition at %1 will be used for starting %2. - + %1 এ EFI সিস্টেম পার্টিশন %2 শুরু করার জন্য ব্যবহার করা হবে। EFI system partition: - + EFI সিস্টেম পার্টিশন: @@ -3040,7 +3041,7 @@ Output: Resize partition %1. - + পার্টিশন %1 পুনঃআকার করুন। @@ -3055,7 +3056,7 @@ Output: The installer failed to resize partition %1 on disk '%2'. - + ইনস্টলার '%2' ডিস্কে %1 পার্টিশন পুনঃআকার করতে ব্যর্থ হয়েছে। @@ -3131,12 +3132,12 @@ Output: Scanning storage devices... - + স্টোরেজ ডিভাইস স্ক্যান করা হচ্ছে... Partitioning - + পার্টিশন করা হচ্ছে @@ -3144,29 +3145,29 @@ Output: Set hostname %1 - + হোস্টের নাম %1 নির্ধারণ করুন Set hostname <strong>%1</strong>. - + হোস্টনাম <strong>%1</strong> নির্ধারণ করুন। Setting hostname %1. - + হোস্টনাম %1 নির্ধারণ করা হচ্ছে। Internal Error - + অভ্যন্তরীণ ত্রুটি Cannot write hostname to target system - + লক্ষ্য ব্যবস্থায় হোস্টের নাম লেখা যাচ্ছে না @@ -3174,24 +3175,24 @@ Output: Set keyboard model to %1, layout to %2-%3 - + কীবোর্ড মডেলটিকে %1, লেআউটটিকে %2-%3 এ সেট করুন Failed to write keyboard configuration for the virtual console. - + ভার্চুয়াল কনসোলের জন্য কীবোর্ড কনফিগারেশন লিখতে ব্যর্থ হয়েছে। Failed to write to %1 - + %1 এ লিখতে ব্যর্থ Failed to write keyboard configuration for X11. - + X11 এর জন্য কীবোর্ড কনফিগারেশন লিখতে ব্যর্থ হয়েছে। @@ -3287,22 +3288,22 @@ Output: Set password for user %1 - + ব্যবহারকারীর জন্য গুপ্ত-সংকেত নির্ধারণ করুন % 1 Setting password for user %1. - + ব্যবহারকারীর %1-এর জন্য গুপ্ত-সংকেত নির্ধারণ করা হচ্ছে। Bad destination system path. - + খারাপ গন্তব্য সিস্টেম পথ। rootMountPoint is %1 - + রুটমাউন্টপয়েন্টটি % 1 @@ -3317,12 +3318,12 @@ Output: Cannot set password for user %1. - + % 1 ব্যবহারকারীর জন্য পাসওয়ার্ড নির্ধারণ করা যাচ্ছে না। usermod terminated with error code %1. - + %1 ত্রুটি কোড দিয়ে ব্যবহারকারীমোড সমাপ্ত করা হয়েছে। @@ -3330,37 +3331,37 @@ Output: Set timezone to %1/%2 - + %1/%2 এ সময়অঞ্চল নির্ধারণ করুন Cannot access selected timezone path. - + নির্বাচিত সময়অঞ্চল পথে প্রবেশ করতে পারে না। Bad path: %1 - + খারাপ পথ: %1 Cannot set timezone. - + সময়অঞ্চল নির্ধারণ করা যাচ্ছে না। Link creation failed, target: %1; link name: %2 - + লিংক তৈরি ব্যর্থ হয়েছে, লক্ষ্য: %1; লিংকের নাম: %2 Cannot set timezone, - + সময়অঞ্চল নির্ধারণ করা যাচ্ছে না, Cannot open /etc/timezone for writing - + লেখার জন্য /ইত্যাদি/সময়অঞ্চল খোলা যাচ্ছে না @@ -3390,7 +3391,7 @@ Output: This is an overview of what will happen once you start the install procedure. - + আপনি ইনস্টল প্রক্রিয়া শুরু করার পর কি হবে তার একটি পর্যালোচনা। @@ -3398,7 +3399,7 @@ Output: Summary - + সারাংশ @@ -3487,7 +3488,7 @@ Output: Form - + ফর্ম @@ -3548,7 +3549,7 @@ Output: Your passwords do not match! - + আপনার পাসওয়ার্ড মেলে না! @@ -3556,7 +3557,7 @@ Output: Users - + ব্যবহারকারীরা @@ -3630,7 +3631,7 @@ Output: Form - + ফর্ম @@ -3641,7 +3642,7 @@ Output: &About - + এবংসম্পর্কে @@ -3661,7 +3662,7 @@ Output: &Support - + এবংসহায়তা @@ -3671,7 +3672,7 @@ Output: &Known issues - + এবংপরিচিত বিষয়গুলো @@ -3681,7 +3682,7 @@ Output: &Release notes - + এবংনোট প্রকাশ করুন @@ -3701,12 +3702,12 @@ Output: <h1>Welcome to the %1 installer.</h1> - + <h1>%1 ইনস্টলারে স্বাগতম।</h1> %1 support - + %1 সহায়তা @@ -3716,7 +3717,7 @@ Output: About %1 installer - + %1 ইনস্টলার সম্পর্কে @@ -3729,7 +3730,7 @@ Output: Welcome - + স্বাগতম @@ -3737,7 +3738,7 @@ Output: Welcome - + স্বাগতম diff --git a/lang/calamares_da.ts b/lang/calamares_da.ts index 31f66bd32..7e6340a46 100644 --- a/lang/calamares_da.ts +++ b/lang/calamares_da.ts @@ -717,7 +717,7 @@ Installationsprogrammet vil stoppe og alle ændringer vil gå tabt. Set timezone to %1/%2. - + Indstil tidszone til %1/%2. @@ -802,7 +802,7 @@ Installationsprogrammet vil stoppe og alle ændringer vil gå tabt. '%1' is not allowed as username. - + '%1' er ikke tilladt som brugernavn. @@ -827,7 +827,7 @@ Installationsprogrammet vil stoppe og alle ændringer vil gå tabt. '%1' is not allowed as hostname. - + '%1' er ikke tilladt som værtsnavn. @@ -3801,7 +3801,8 @@ setting <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Lokaliteter</h1> </br> + Systemets lokalitetsindstillinger påvirker tal- og datoformater. Den nuværende indstilling er <strong>%1</strong>. diff --git a/lang/calamares_en.ts b/lang/calamares_en.ts index e6e44b274..e4773535e 100644 --- a/lang/calamares_en.ts +++ b/lang/calamares_en.ts @@ -510,134 +510,134 @@ The installer will quit and all changes will be lost. Form - + Select storage de&vice: Select storage de&vice: - - - - + + + + Current: Current: - + After: After: - + <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + Reuse %1 as home partition for %2. Reuse %1 as home partition for %2. - + <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> - + %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. - + Boot loader location: Boot loader location: - + <strong>Select a partition to install on</strong> <strong>Select a partition to install on</strong> - + An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. - + The EFI system partition at %1 will be used for starting %2. The EFI system partition at %1 will be used for starting %2. - + EFI system partition: EFI system partition: - + This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - - - - + + + + <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. - - - - + + + + <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. - - - - + + + + <strong>Replace a partition</strong><br/>Replaces a partition with %1. <strong>Replace a partition</strong><br/>Replaces a partition with %1. - + This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + No Swap No Swap - + Reuse Swap Reuse Swap - + Swap (no Hibernate) Swap (no Hibernate) - + Swap (with Hibernate) Swap (with Hibernate) - + Swap to file Swap to file @@ -715,17 +715,17 @@ The installer will quit and all changes will be lost. Set keyboard layout to %1/%2. - + Set timezone to %1/%2. Set timezone to %1/%2. - + The system language will be set to %1. The system language will be set to %1. - + The numbers and dates locale will be set to %1. The numbers and dates locale will be set to %1. @@ -795,42 +795,42 @@ The installer will quit and all changes will be lost. <h1>Welcome to the %1 installer</h1> - + Your username is too long. Your username is too long. - + '%1' is not allowed as username. '%1' is not allowed as username. - + Your username must start with a lowercase letter or underscore. Your username must start with a lowercase letter or underscore. - + Only lowercase letters, numbers, underscore and hyphen are allowed. Only lowercase letters, numbers, underscore and hyphen are allowed. - + Your hostname is too short. Your hostname is too short. - + Your hostname is too long. Your hostname is too long. - + '%1' is not allowed as hostname. '%1' is not allowed as hostname. - + Only letters, numbers, underscore and hyphen are allowed. Only letters, numbers, underscore and hyphen are allowed. @@ -1251,7 +1251,8 @@ The installer will quit and all changes will be lost. Confirm passphrase - + + Please enter the same passphrase in both boxes. Please enter the same passphrase in both boxes. @@ -1703,18 +1704,18 @@ The installer will quit and all changes will be lost. LocalePage - + Region: Region: - + Zone: Zone: - - + + &Change... &Change... @@ -1792,7 +1793,12 @@ The installer will quit and all changes will be lost. Map - + + Timezone: %1 + Timezone: %1 + + + Please select your preferred location on the map so the installer can suggest the locale and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming. @@ -1955,247 +1961,247 @@ The installer will quit and all changes will be lost. PWQ - + Password is too short Password is too short - + Password is too long Password is too long - + Password is too weak Password is too weak - + Memory allocation error when setting '%1' Memory allocation error when setting '%1' - + Memory allocation error Memory allocation error - + The password is the same as the old one The password is the same as the old one - + The password is a palindrome The password is a palindrome - + The password differs with case changes only The password differs with case changes only - + The password is too similar to the old one The password is too similar to the old one - + The password contains the user name in some form The password contains the user name in some form - + The password contains words from the real name of the user in some form The password contains words from the real name of the user in some form - + The password contains forbidden words in some form The password contains forbidden words in some form - + The password contains less than %1 digits The password contains less than %1 digits - + The password contains too few digits The password contains too few digits - + The password contains less than %1 uppercase letters The password contains less than %1 uppercase letters - + The password contains too few uppercase letters The password contains too few uppercase letters - + The password contains less than %1 lowercase letters The password contains less than %1 lowercase letters - + The password contains too few lowercase letters The password contains too few lowercase letters - + The password contains less than %1 non-alphanumeric characters The password contains less than %1 non-alphanumeric characters - + The password contains too few non-alphanumeric characters The password contains too few non-alphanumeric characters - + The password is shorter than %1 characters The password is shorter than %1 characters - + The password is too short The password is too short - + The password is just rotated old one The password is just rotated old one - + The password contains less than %1 character classes The password contains less than %1 character classes - + The password does not contain enough character classes The password does not contain enough character classes - + The password contains more than %1 same characters consecutively The password contains more than %1 same characters consecutively - + The password contains too many same characters consecutively The password contains too many same characters consecutively - + The password contains more than %1 characters of the same class consecutively The password contains more than %1 characters of the same class consecutively - + The password contains too many characters of the same class consecutively The password contains too many characters of the same class consecutively - + The password contains monotonic sequence longer than %1 characters The password contains monotonic sequence longer than %1 characters - + The password contains too long of a monotonic character sequence The password contains too long of a monotonic character sequence - + No password supplied No password supplied - + Cannot obtain random numbers from the RNG device Cannot obtain random numbers from the RNG device - + Password generation failed - required entropy too low for settings Password generation failed - required entropy too low for settings - + The password fails the dictionary check - %1 The password fails the dictionary check - %1 - + The password fails the dictionary check The password fails the dictionary check - + Unknown setting - %1 Unknown setting - %1 - + Unknown setting Unknown setting - + Bad integer value of setting - %1 Bad integer value of setting - %1 - + Bad integer value Bad integer value - + Setting %1 is not of integer type Setting %1 is not of integer type - + Setting is not of integer type Setting is not of integer type - + Setting %1 is not of string type Setting %1 is not of string type - + Setting is not of string type Setting is not of string type - + Opening the configuration file failed Opening the configuration file failed - + The configuration file is malformed The configuration file is malformed - + Fatal failure Fatal failure - + Unknown error Unknown error - + Password is empty Password is empty @@ -2529,67 +2535,67 @@ The installer will quit and all changes will be lost. PartitionViewStep - + Gathering system information... Gathering system information... - + Partitions Partitions - + Install %1 <strong>alongside</strong> another operating system. Install %1 <strong>alongside</strong> another operating system. - + <strong>Erase</strong> disk and install %1. <strong>Erase</strong> disk and install %1. - + <strong>Replace</strong> a partition with %1. <strong>Replace</strong> a partition with %1. - + <strong>Manual</strong> partitioning. <strong>Manual</strong> partitioning. - + Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). - + <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. - + <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. - + <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). - + Disk <strong>%1</strong> (%2) Disk <strong>%1</strong> (%2) - + Current: Current: - + After: After: @@ -2634,12 +2640,12 @@ The installer will quit and all changes will be lost. A separate boot partition was set up together with an encrypted root partition, but the boot partition is not encrypted.<br/><br/>There are security concerns with this kind of setup, because important system files are kept on an unencrypted partition.<br/>You may continue if you wish, but filesystem unlocking will happen later during system startup.<br/>To encrypt the boot partition, go back and recreate it, selecting <strong>Encrypt</strong> in the partition creation window. - + has at least one disk device available. has at least one disk device available. - + There are no partitions to install on. There are no partitions to install on. @@ -3547,17 +3553,17 @@ Output: UsersPage - + <small>If more than one person will use this computer, you can create multiple accounts after setup.</small> <small>If more than one person will use this computer, you can create multiple accounts after setup.</small> - + <small>If more than one person will use this computer, you can create multiple accounts after installation.</small> <small>If more than one person will use this computer, you can create multiple accounts after installation.</small> - + Your passwords do not match! Your passwords do not match! @@ -3565,7 +3571,7 @@ Output: UsersViewStep - + Users Users @@ -3856,7 +3862,7 @@ Output: localeq - + Change Change diff --git a/lang/calamares_hi.ts b/lang/calamares_hi.ts index c651eaea9..b1d7b3ce8 100644 --- a/lang/calamares_hi.ts +++ b/lang/calamares_hi.ts @@ -530,7 +530,7 @@ The installer will quit and all changes will be lost. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - <strong>मैनुअल विभाजन</strong><br/> आप स्वयं भी विभाजन बना व उनका आकार बदल सकते है। + <strong>मैनुअल विभाजन</strong><br/> स्वयं विभाजन बनाएँ या उनका आकार बदलें। @@ -717,7 +717,7 @@ The installer will quit and all changes will be lost. Set timezone to %1/%2. - + समय क्षेत्र %1%2 सेट करें। @@ -797,42 +797,42 @@ The installer will quit and all changes will be lost. Your username is too long. - आपका उपयोक्ता नाम बहुत लंबा है। + उपयोक्ता नाम बहुत लंबा है। '%1' is not allowed as username. - + उपयोक्ता नाम के रूप में '%1' का उपयोग अस्वीकार्य है। Your username must start with a lowercase letter or underscore. - आपके उपयोक्ता नाम का आरंभ lowercase अक्षर या अंडरस्कोर(_) से ही होना चाहिए। + उपयोक्ता नाम का आरंभ केवल लोअरकेस अक्षर या अंडरस्कोर(-) से ही करें। Only lowercase letters, numbers, underscore and hyphen are allowed. - केवल lowercase अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) का उपयोग ही मान्य है। + केवल लोअरकेस अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) ही स्वीकार्य हैं। Your hostname is too short. - आपका होस्ट नाम बहुत छोटा है। + होस्ट नाम बहुत छोटा है। Your hostname is too long. - आपका होस्ट नाम बहुत लंबा है। + होस्ट नाम बहुत लंबा है। '%1' is not allowed as hostname. - + होस्ट नाम के रूप में '%1' का उपयोग अस्वीकार्य है। Only letters, numbers, underscore and hyphen are allowed. - केवल अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) का उपयोग ही मान्य है। + केवल अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) ही स्वीकार्य हैं। @@ -3799,7 +3799,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>स्थानिकी</h1> </br> + सिस्टम स्थानिकी सेटिंग संख्या व दिनांक के प्रारूप को प्रभावित करती है। वर्तमान सेटिंग <strong>%1</strong> है। diff --git a/lang/calamares_ja.ts b/lang/calamares_ja.ts index fc20c54b3..8febc0aba 100644 --- a/lang/calamares_ja.ts +++ b/lang/calamares_ja.ts @@ -715,7 +715,7 @@ The installer will quit and all changes will be lost. Set timezone to %1/%2. - + タイムゾーンを %1/%2 に設定します。 @@ -725,7 +725,7 @@ The installer will quit and all changes will be lost. The numbers and dates locale will be set to %1. - 数字と日付のロケールを %1 に設定します。 + 数値と日付のロケールを %1 に設定します。 @@ -800,7 +800,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as username. - + '%1' はユーザー名として許可されていません。 @@ -825,7 +825,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as hostname. - + '%1' はホスト名として許可されていません。 @@ -3799,7 +3799,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>ロケール</h1> </br> + システムのロケール設定は、数値と日付の形式に影響を及ぼします。現在の設定は <strong>%1</strong> です。 diff --git a/lang/calamares_sv.ts b/lang/calamares_sv.ts index 4c6a523e6..c1bc83bc7 100644 --- a/lang/calamares_sv.ts +++ b/lang/calamares_sv.ts @@ -137,7 +137,7 @@ Programmed job failure was explicitly requested. - + Programmerat jobbfel begärdes uttryckligen. @@ -222,7 +222,7 @@ Loading failed. - + Laddning misslyckades. @@ -716,7 +716,7 @@ Alla ändringar kommer att gå förlorade. Set timezone to %1/%2. - + Sätt tidszon till %1/%2. @@ -776,22 +776,22 @@ Alla ändringar kommer att gå förlorade. <h1>Welcome to the Calamares setup program for %1</h1> - + <h1>Välkommen till Calamares installationsprogram för %1</h1> <h1>Welcome to %1 setup</h1> - + <h1>Välkommen till %1 installation</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>Välkommen till Calamares installationsprogram för %1</h1> <h1>Welcome to the %1 installer</h1> - + <h1>Välkommen till %1-installeraren</h1> @@ -801,7 +801,7 @@ Alla ändringar kommer att gå förlorade. '%1' is not allowed as username. - + '%1' är inte tillåtet som användarnamn. @@ -826,7 +826,7 @@ Alla ändringar kommer att gå förlorade. '%1' is not allowed as hostname. - + '%1' är inte tillåtet som värdnamn. @@ -839,7 +839,7 @@ Alla ändringar kommer att gå förlorade. Contextual Processes Job - + Kontextuellt processjobb @@ -1476,7 +1476,7 @@ Alla ändringar kommer att gå förlorade. OEM Batch Identifier - + OEM-batchidentifierare @@ -1795,7 +1795,10 @@ Alla ändringar kommer att gå förlorade. Please select your preferred location on the map so the installer can suggest the locale and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming. - + Snälla välj din föredragna plats på kartan så installationsprogrammet kan föreslå nationella inställningar + och tidszons inställningar åt dig. Du kan finjustera de föreslagna inställningarna nedan. +Sök på kartan genom att dra + för att flytta och använd +/- knapparna för att zooma in/ut eller så använder du musens scrollhjul för att zooma. @@ -1910,12 +1913,12 @@ Alla ändringar kommer att gå förlorade. Ba&tch: - + Gr&upp: <html><head/><body><p>Enter a batch-identifier here. This will be stored in the target system.</p></body></html> - + <html><head/><body><p>Ange en batch-identifierare här. Den kommer lagras på målsystemet.</p></body></html> @@ -1933,7 +1936,7 @@ Alla ändringar kommer att gå förlorade. Set the OEM Batch Identifier to <code>%1</code>. - + Sätt OEM-batchidentifierare till <code>%1</code>. @@ -1946,7 +1949,7 @@ Alla ändringar kommer att gå förlorade. To be able to select a timezone, make sure you are connected to the internet. Restart the installer after connecting. You can fine-tune Language and Locale settings below. - + För att kunna välja en tidszon, se till att du är ansluten till internet. Starta om installationsprogrammet efter anslutningen. Du kan finjustera språk och nationella inställningar nedan. @@ -1969,7 +1972,7 @@ Alla ändringar kommer att gå förlorade. Memory allocation error when setting '%1' - + Minnesallokerings fel då '%1' skulle ställas in @@ -2144,7 +2147,7 @@ Alla ändringar kommer att gå förlorade. Bad integer value of setting - %1 - + Dåligt heltals värde på inställning - %1 @@ -2851,7 +2854,8 @@ Utdata: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Denna dator uppfyller inte alla rekommenderade krav för att installera %1. +<br/>Installationen kan fortsätta, men alla alternativ och funktioner kanske inte kan användas.</p> @@ -2962,13 +2966,15 @@ Utdata: <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> - + <p>Denna dator uppfyller inte minimikraven för att installera %1.<br/> +Installationen kan inte fortsätta.</p> <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Denna dator uppfyller inte alla rekommenderade krav för att installera %1. +<br/>Installationen kan fortsätta, men alla alternativ och funktioner kanske inte kan användas.</p> @@ -3411,17 +3417,17 @@ Utdata: Installation feedback - + Installationsåterkoppling Sending installation feedback. - + Skickar installationsåterkoppling Internal error in install-tracking. - + Internt fel i install-tracking. @@ -3434,28 +3440,28 @@ Utdata: KDE user feedback - + KDE användarfeedback Configuring KDE user feedback. - + Konfigurerar KDE användarfeedback. Error in KDE user feedback configuration. - + Fel vid konfigurering av KDE användarfeedback. Could not configure KDE user feedback correctly, script error %1. - + Kunde inte konfigurera KDE användarfeedback korrekt, script fel %1. Could not configure KDE user feedback correctly, Calamares error %1. - + Kunde inte konfigurera KDE användarfeedback korrekt, Calamares fel %1. @@ -3474,7 +3480,7 @@ Utdata: Error in machine feedback configuration. - + Fel vid konfigurering av maskin feedback @@ -3502,32 +3508,32 @@ Utdata: <html><head/><body><p>Click here to send <span style=" font-weight:600;">no information at all</span> about your installation.</p></body></html> - + <html><head/><body><p>Klicka här för att inte <span style=" font-weight:600;"> skicka någon information alls om din installation.</p></body></html> <html><head/><body><p><a href="placeholder"><span style=" text-decoration: underline; color:#2980b9;">Click here for more information about user feedback</span></a></p></body></html> - + <html><head/><body><p><a href="placeholder"><span style=" text-decoration: underline; color:#2980b9;"> Klicka här för information om användarfeedback </span></a></p></body></html> Tracking helps %1 to see how often it is installed, what hardware it is installed on and which applications are used. To see what will be sent, please click the help icon next to each area. - + Spårning hjälper %1 att se hur ofta den är installerad, vilken hårdvara den är installerad på och vilka program som används. För att se vad som skickas, Klicka på hjälp ikonen vad sidan av varje område för att se vad som skickas. By selecting this you will send information about your installation and hardware. This information will only be sent <b>once</b> after the installation finishes. - + Genom att välja detta, kommer du skicka information om din installation och hårdvara. Denna information kommer <b>enbart skickas en gång</b> efter att installationen slutförts. By selecting this you will periodically send information about your <b>machine</b> installation, hardware and applications, to %1. - + Genom att välja detta, kommer du periodiskt skicka information om din <b>maskin</b>installation, hårdvara och program, till %1 By selecting this you will regularly send information about your <b>user</b> installation, hardware, applications and application usage patterns, to %1. - + Genom att välja detta, kommer du regelbundet skicka information om din <b>användar</b>installation, hårdvara, program och dina program användningsmönster till %1. @@ -3786,13 +3792,15 @@ Utdata: <h1>Languages</h1> </br> The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>. - + <h1>Språk</h1> </br> +Systemspråket påverkar vilket språk och teckenuppsättning somliga kommandoradsprogram använder. Den nuvarande inställningen är <strong>%1</strong>. <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Nationella inställningar</h1> </br> +Systems nationella inställningar påverkar nummer och datumformat. Den nuvarande inställningen är <strong>%3</strong>. diff --git a/lang/python.pot b/lang/python.pot index 1c20ae7b5..7e3d85b67 100644 --- a/lang/python.pot +++ b/lang/python.pot @@ -2,29 +2,29 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-07-29 11:03+0200\n" +"POT-Creation-Date: 2020-08-09 20:56+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: \n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: src/modules/grubcfg/main.py:37 msgid "Configure GRUB." -msgstr "Configure GRUB." +msgstr "" #: src/modules/mount/main.py:38 msgid "Mounting partitions." -msgstr "Mounting partitions." +msgstr "" #: src/modules/mount/main.py:150 src/modules/initcpiocfg/main.py:205 #: src/modules/initcpiocfg/main.py:209 @@ -36,179 +36,172 @@ msgstr "Mounting partitions." #: src/modules/fstab/main.py:338 src/modules/localecfg/main.py:144 #: src/modules/networkcfg/main.py:48 msgid "Configuration Error" -msgstr "Configuration Error" +msgstr "" #: src/modules/mount/main.py:151 src/modules/initcpiocfg/main.py:206 #: src/modules/luksopenswaphookcfg/main.py:96 src/modules/rawfs/main.py:174 #: src/modules/initramfscfg/main.py:95 src/modules/openrcdmcryptcfg/main.py:79 #: src/modules/fstab/main.py:333 msgid "No partitions are defined for
{!s}
to use." -msgstr "No partitions are defined for
{!s}
to use." +msgstr "" #: src/modules/services-systemd/main.py:35 msgid "Configure systemd services" -msgstr "Configure systemd services" +msgstr "" #: src/modules/services-systemd/main.py:68 #: src/modules/services-openrc/main.py:102 msgid "Cannot modify service" -msgstr "Cannot modify service" +msgstr "" #: src/modules/services-systemd/main.py:69 msgid "" "systemctl {arg!s} call in chroot returned error code {num!s}." msgstr "" -"systemctl {arg!s} call in chroot returned error code {num!s}." #: src/modules/services-systemd/main.py:72 #: src/modules/services-systemd/main.py:76 msgid "Cannot enable systemd service {name!s}." -msgstr "Cannot enable systemd service {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:74 msgid "Cannot enable systemd target {name!s}." -msgstr "Cannot enable systemd target {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:78 msgid "Cannot disable systemd target {name!s}." -msgstr "Cannot disable systemd target {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:80 msgid "Cannot mask systemd unit {name!s}." -msgstr "Cannot mask systemd unit {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:82 msgid "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." +"Unknown systemd commands {command!s} and {suffix!s} for unit {name!s}." msgstr "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." #: src/modules/umount/main.py:40 msgid "Unmount file systems." -msgstr "Unmount file systems." +msgstr "" #: src/modules/unpackfs/main.py:44 msgid "Filling up filesystems." -msgstr "Filling up filesystems." +msgstr "" #: src/modules/unpackfs/main.py:257 msgid "rsync failed with error code {}." -msgstr "rsync failed with error code {}." +msgstr "" #: src/modules/unpackfs/main.py:302 msgid "Unpacking image {}/{}, file {}/{}" -msgstr "Unpacking image {}/{}, file {}/{}" +msgstr "" #: src/modules/unpackfs/main.py:317 msgid "Starting to unpack {}" -msgstr "Starting to unpack {}" +msgstr "" #: src/modules/unpackfs/main.py:326 src/modules/unpackfs/main.py:448 msgid "Failed to unpack image \"{}\"" -msgstr "Failed to unpack image \"{}\"" +msgstr "" #: src/modules/unpackfs/main.py:415 msgid "No mount point for root partition" -msgstr "No mount point for root partition" +msgstr "" #: src/modules/unpackfs/main.py:416 msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing" -msgstr "globalstorage does not contain a \"rootMountPoint\" key, doing nothing" +msgstr "" #: src/modules/unpackfs/main.py:421 msgid "Bad mount point for root partition" -msgstr "Bad mount point for root partition" +msgstr "" #: src/modules/unpackfs/main.py:422 msgid "rootMountPoint is \"{}\", which does not exist, doing nothing" -msgstr "rootMountPoint is \"{}\", which does not exist, doing nothing" +msgstr "" #: src/modules/unpackfs/main.py:438 src/modules/unpackfs/main.py:442 #: src/modules/unpackfs/main.py:462 msgid "Bad unsquash configuration" -msgstr "Bad unsquash configuration" +msgstr "" #: src/modules/unpackfs/main.py:439 msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel" -msgstr "The filesystem for \"{}\" ({}) is not supported by your current kernel" +msgstr "" #: src/modules/unpackfs/main.py:443 msgid "The source filesystem \"{}\" does not exist" -msgstr "The source filesystem \"{}\" does not exist" +msgstr "" #: src/modules/unpackfs/main.py:449 msgid "" "Failed to find unsquashfs, make sure you have the squashfs-tools package " "installed" msgstr "" -"Failed to find unsquashfs, make sure you have the squashfs-tools package " -"installed" #: src/modules/unpackfs/main.py:463 msgid "The destination \"{}\" in the target system is not a directory" -msgstr "The destination \"{}\" in the target system is not a directory" +msgstr "" #: src/modules/displaymanager/main.py:523 msgid "Cannot write KDM configuration file" -msgstr "Cannot write KDM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:524 msgid "KDM config file {!s} does not exist" -msgstr "KDM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:585 msgid "Cannot write LXDM configuration file" -msgstr "Cannot write LXDM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:586 msgid "LXDM config file {!s} does not exist" -msgstr "LXDM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:669 msgid "Cannot write LightDM configuration file" -msgstr "Cannot write LightDM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:670 msgid "LightDM config file {!s} does not exist" -msgstr "LightDM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:744 msgid "Cannot configure LightDM" -msgstr "Cannot configure LightDM" +msgstr "" #: src/modules/displaymanager/main.py:745 msgid "No LightDM greeter installed." -msgstr "No LightDM greeter installed." +msgstr "" #: src/modules/displaymanager/main.py:776 msgid "Cannot write SLIM configuration file" -msgstr "Cannot write SLIM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:777 msgid "SLIM config file {!s} does not exist" -msgstr "SLIM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:903 msgid "No display managers selected for the displaymanager module." -msgstr "No display managers selected for the displaymanager module." +msgstr "" #: src/modules/displaymanager/main.py:904 msgid "" "The displaymanagers list is empty or undefined in bothglobalstorage and " "displaymanager.conf." msgstr "" -"The displaymanagers list is empty or undefined in bothglobalstorage and " -"displaymanager.conf." #: src/modules/displaymanager/main.py:986 msgid "Display manager configuration was incomplete" -msgstr "Display manager configuration was incomplete" +msgstr "" #: src/modules/initcpiocfg/main.py:37 msgid "Configuring mkinitcpio." -msgstr "Configuring mkinitcpio." +msgstr "" #: src/modules/initcpiocfg/main.py:210 #: src/modules/luksopenswaphookcfg/main.py:100 @@ -216,139 +209,131 @@ msgstr "Configuring mkinitcpio." #: src/modules/fstab/main.py:339 src/modules/localecfg/main.py:145 #: src/modules/networkcfg/main.py:49 msgid "No root mount point is given for
{!s}
to use." -msgstr "No root mount point is given for
{!s}
to use." +msgstr "" #: src/modules/luksopenswaphookcfg/main.py:35 msgid "Configuring encrypted swap." -msgstr "Configuring encrypted swap." +msgstr "" #: src/modules/rawfs/main.py:35 msgid "Installing data." -msgstr "Installing data." +msgstr "" #: src/modules/services-openrc/main.py:38 msgid "Configure OpenRC services" -msgstr "Configure OpenRC services" +msgstr "" #: src/modules/services-openrc/main.py:66 msgid "Cannot add service {name!s} to run-level {level!s}." -msgstr "Cannot add service {name!s} to run-level {level!s}." +msgstr "" #: src/modules/services-openrc/main.py:68 msgid "Cannot remove service {name!s} from run-level {level!s}." -msgstr "Cannot remove service {name!s} from run-level {level!s}." +msgstr "" #: src/modules/services-openrc/main.py:70 msgid "" "Unknown service-action {arg!s} for service {name!s} in run-" "level {level!s}." msgstr "" -"Unknown service-action {arg!s} for service {name!s} in run-" -"level {level!s}." #: src/modules/services-openrc/main.py:103 msgid "" "rc-update {arg!s} call in chroot returned error code {num!s}." msgstr "" -"rc-update {arg!s} call in chroot returned error code {num!s}." #: src/modules/services-openrc/main.py:110 msgid "Target runlevel does not exist" -msgstr "Target runlevel does not exist" +msgstr "" #: src/modules/services-openrc/main.py:111 msgid "" "The path for runlevel {level!s} is {path!s}, which does not " "exist." msgstr "" -"The path for runlevel {level!s} is {path!s}, which does not " -"exist." #: src/modules/services-openrc/main.py:119 msgid "Target service does not exist" -msgstr "Target service does not exist" +msgstr "" #: src/modules/services-openrc/main.py:120 msgid "" -"The path for service {name!s} is {path!s}, which does not " -"exist." +"The path for service {name!s} is {path!s}, which does not exist." msgstr "" -"The path for service {name!s} is {path!s}, which does not " -"exist." #: src/modules/plymouthcfg/main.py:36 msgid "Configure Plymouth theme" -msgstr "Configure Plymouth theme" +msgstr "" #: src/modules/packages/main.py:59 src/modules/packages/main.py:68 #: src/modules/packages/main.py:78 msgid "Install packages." -msgstr "Install packages." +msgstr "" #: src/modules/packages/main.py:66 #, python-format msgid "Processing packages (%(count)d / %(total)d)" -msgstr "Processing packages (%(count)d / %(total)d)" +msgstr "" #: src/modules/packages/main.py:71 #, python-format msgid "Installing one package." msgid_plural "Installing %(num)d packages." -msgstr[0] "Installing one package." -msgstr[1] "Installing %(num)d packages." +msgstr[0] "" +msgstr[1] "" #: src/modules/packages/main.py:74 #, python-format msgid "Removing one package." msgid_plural "Removing %(num)d packages." -msgstr[0] "Removing one package." -msgstr[1] "Removing %(num)d packages." +msgstr[0] "" +msgstr[1] "" #: src/modules/bootloader/main.py:51 msgid "Install bootloader." -msgstr "Install bootloader." +msgstr "" #: src/modules/hwclock/main.py:35 msgid "Setting hardware clock." -msgstr "Setting hardware clock." +msgstr "" #: src/modules/dracut/main.py:36 msgid "Creating initramfs with dracut." -msgstr "Creating initramfs with dracut." +msgstr "" #: src/modules/dracut/main.py:58 msgid "Failed to run dracut on the target" -msgstr "Failed to run dracut on the target" +msgstr "" #: src/modules/dracut/main.py:59 msgid "The exit code was {}" -msgstr "The exit code was {}" +msgstr "" #: src/modules/initramfscfg/main.py:41 msgid "Configuring initramfs." -msgstr "Configuring initramfs." +msgstr "" #: src/modules/openrcdmcryptcfg/main.py:34 msgid "Configuring OpenRC dmcrypt service." -msgstr "Configuring OpenRC dmcrypt service." +msgstr "" #: src/modules/fstab/main.py:38 msgid "Writing fstab." -msgstr "Writing fstab." +msgstr "" #: src/modules/dummypython/main.py:44 msgid "Dummy python job." -msgstr "Dummy python job." +msgstr "" #: src/modules/dummypython/main.py:46 src/modules/dummypython/main.py:102 #: src/modules/dummypython/main.py:103 msgid "Dummy python step {}" -msgstr "Dummy python step {}" +msgstr "" #: src/modules/localecfg/main.py:39 msgid "Configuring locales." -msgstr "Configuring locales." +msgstr "" #: src/modules/networkcfg/main.py:37 msgid "Saving network configuration." -msgstr "Saving network configuration." +msgstr "" diff --git a/lang/python/ko/LC_MESSAGES/python.mo b/lang/python/ko/LC_MESSAGES/python.mo index 22a53a688..b15abd450 100644 Binary files a/lang/python/ko/LC_MESSAGES/python.mo and b/lang/python/ko/LC_MESSAGES/python.mo differ diff --git a/lang/python/ko/LC_MESSAGES/python.po b/lang/python/ko/LC_MESSAGES/python.po index 070af8d10..d79730f42 100644 --- a/lang/python/ko/LC_MESSAGES/python.po +++ b/lang/python/ko/LC_MESSAGES/python.po @@ -5,7 +5,7 @@ # # Translators: # 김지현 , 2018 -# MarongHappy , 2020 +# JungHee Lee , 2020 # #, fuzzy msgid "" @@ -14,7 +14,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-07-29 11:03+0200\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: MarongHappy , 2020\n" +"Last-Translator: JungHee Lee , 2020\n" "Language-Team: Korean (https://www.transifex.com/calamares/teams/20061/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 8e209f8a3..dc05270af 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -1,6 +1,7 @@ # === This file is part of Calamares - === # # SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later # # Calamares is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,12 +16,12 @@ # You should have received a copy of the GNU General Public License # along with Calamares. If not, see . # -# SPDX-License-Identifier: GPL-3.0-or-later -# License-Filename: LICENSE -# + # # libcalamares is the non-GUI part of Calamares, which includes handling -# translations, configurations, logging, utilities, global storage, and (non-GUI) jobs. +# translations, configurations, logging, utilities, global storage, and +# (non-GUI) jobs. +# add_definitions( -DDLLEXPORT_PRO ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) @@ -56,6 +57,7 @@ set( libSources locale/Lookup.cpp locale/TimeZone.cpp locale/TranslatableConfiguration.cpp + locale/TranslatableString.cpp # Modules modulesystem/InstanceKey.cpp @@ -217,18 +219,24 @@ endforeach() # calamares_add_test( libcalamarestest + SOURCES + Tests.cpp +) + +calamares_add_test( + libcalamaresutilstest SOURCES utils/Tests.cpp ) calamares_add_test( - libcalamarestestpaths + libcalamaresutilspathstest SOURCES utils/TestPaths.cpp ) calamares_add_test( - geoiptest + libcalamaresgeoiptest SOURCES geoip/GeoIPTests.cpp ${geoip_src} diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index 341fc3892..253a4d6ad 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -2,6 +2,7 @@ * * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +17,9 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "GlobalStorage.h" -#include "JobQueue.h" #include "utils/Logger.h" #include "utils/Units.h" @@ -30,12 +27,35 @@ #include #include +#include using CalamaresUtils::operator""_MiB; namespace Calamares { +class GlobalStorage::ReadLock : public QMutexLocker +{ +public: + ReadLock( const GlobalStorage* gs ) + : QMutexLocker( &gs->m_mutex ) + { + } +}; + +class GlobalStorage::WriteLock : public QMutexLocker +{ +public: + WriteLock( GlobalStorage* gs ) + : QMutexLocker( &gs->m_mutex ) + , m_gs( gs ) + { + } + ~WriteLock() { m_gs->changed(); } + + GlobalStorage* m_gs; +}; + GlobalStorage::GlobalStorage( QObject* parent ) : QObject( parent ) { @@ -45,6 +65,7 @@ GlobalStorage::GlobalStorage( QObject* parent ) bool GlobalStorage::contains( const QString& key ) const { + ReadLock l( this ); return m.contains( key ); } @@ -52,6 +73,7 @@ GlobalStorage::contains( const QString& key ) const int GlobalStorage::count() const { + ReadLock l( this ); return m.count(); } @@ -59,14 +81,15 @@ GlobalStorage::count() const void GlobalStorage::insert( const QString& key, const QVariant& value ) { + WriteLock l( this ); m.insert( key, value ); - emit changed(); } QStringList GlobalStorage::keys() const { + ReadLock l( this ); return m.keys(); } @@ -74,8 +97,8 @@ GlobalStorage::keys() const int GlobalStorage::remove( const QString& key ) { + WriteLock l( this ); int nItems = m.remove( key ); - emit changed(); return nItems; } @@ -83,21 +106,25 @@ GlobalStorage::remove( const QString& key ) QVariant GlobalStorage::value( const QString& key ) const { + ReadLock l( this ); return m.value( key ); } void GlobalStorage::debugDump() const { + ReadLock l( this ); + cDebug() << "GlobalStorage" << Logger::Pointer( this ) << m.count() << "items"; for ( auto it = m.cbegin(); it != m.cend(); ++it ) { - cDebug() << it.key() << '\t' << it.value(); + cDebug() << Logger::SubEntry << it.key() << '\t' << it.value(); } } bool -GlobalStorage::save( const QString& filename ) +GlobalStorage::saveJson( const QString& filename ) const { + ReadLock l( this ); QFile f( filename ); if ( !f.open( QFile::WriteOnly ) ) { @@ -110,7 +137,7 @@ GlobalStorage::save( const QString& filename ) } bool -GlobalStorage::load( const QString& filename ) +GlobalStorage::loadJson( const QString& filename ) { QFile f( filename ); if ( !f.open( QFile::ReadOnly ) ) @@ -130,10 +157,14 @@ GlobalStorage::load( const QString& filename ) } else { + WriteLock l( this ); + // Do **not** use method insert() here, because it would + // recursively lock the mutex, leading to deadlock. Also, + // that would emit changed() for each key. auto map = d.toVariant().toMap(); for ( auto i = map.constBegin(); i != map.constEnd(); ++i ) { - insert( i.key(), *i ); + m.insert( i.key(), *i ); } return true; } @@ -141,8 +172,9 @@ GlobalStorage::load( const QString& filename ) } bool -GlobalStorage::saveYaml( const QString& filename ) +GlobalStorage::saveYaml( const QString& filename ) const { + ReadLock l( this ); return CalamaresUtils::saveYaml( filename, m ); } @@ -150,12 +182,20 @@ bool GlobalStorage::loadYaml( const QString& filename ) { bool ok = false; - auto gs = CalamaresUtils::loadYaml( filename, &ok ); + auto map = CalamaresUtils::loadYaml( filename, &ok ); if ( ok ) { - m = gs; + WriteLock l( this ); + // Do **not** use method insert() here, because it would + // recursively lock the mutex, leading to deadlock. Also, + // that would emit changed() for each key. + for ( auto i = map.constBegin(); i != map.constEnd(); ++i ) + { + m.insert( i.key(), *i ); + } + return true; } - return ok; + return false; } diff --git a/src/libcalamares/GlobalStorage.h b/src/libcalamares/GlobalStorage.h index a2848f888..a12d78978 100644 --- a/src/libcalamares/GlobalStorage.h +++ b/src/libcalamares/GlobalStorage.h @@ -2,6 +2,7 @@ * * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,16 +17,12 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #ifndef CALAMARES_GLOBALSTORAGE_H #define CALAMARES_GLOBALSTORAGE_H -#include "CalamaresConfig.h" - +#include #include #include #include @@ -33,67 +30,146 @@ namespace Calamares { -class DebugWindow; - +/** @brief Storage for data that passes between Calamares modules. + * + * The Global Storage is global to the Calamars JobQueue and + * everything that depends on that: all of its modules use the + * same instance of the JobQueue, and so of the Global Storage. + * + * GS is used to pass data between modules; there is only convention + * about what keys are used, and individual modules should document + * what they put in to GS or what they expect to find in it. + * + * GS behaves as a basic key-value store, with a QVariantMap behind + * it. Any QVariant can be put into the storage, and the signal + * changed() is emitted when any data is modified. + * + * In general, see QVariantMap (possibly after calling data()) for details. + * + * This class is thread-safe -- most accesses go through JobQueue, which + * handles threading itself, but because modules load in parallel and can + * have asynchronous tasks like GeoIP lookups, the storage itself also + * has locking. All methods are thread-safe, use data() to make a snapshot + * copy for use outside of the thread-safe API. + */ class GlobalStorage : public QObject { Q_OBJECT public: + /** @brief Create a GS object + * + * **Generally** there is only one GS object (hence, "global") which + * is owned by the JobQueue instance (which is a singleton). However, + * it is possible to create more GS objects. + */ explicit GlobalStorage( QObject* parent = nullptr ); - //NOTE: thread safety is guaranteed by JobQueue, which executes jobs one by one. - // If at any time jobs become concurrent, this class must be made thread-safe. + /** @brief Insert a key and value into the store + * + * The @p value is added to the store with key @p key. If @p key + * already exists in the store, its existing value is overwritten. + * The changed() signal is emitted regardless. + */ void insert( const QString& key, const QVariant& value ); + /** @brief Removes a key and its value + * + * The @p key is removed from the store. If the @p key does not + * exist, nothing happens. changed() is emitted regardless. + * + * @return the number of keys remaining + */ int remove( const QString& key ); - /// @brief dump keys and values to the debug log + /** @brief dump keys and values to the debug log + * + * All the keys and their values are written to the debug log. + * See save() for caveats: this can leak sensitive information. + */ void debugDump() const; /** @brief write as JSON to the given filename + * + * The file named @p filename is overwritten with a JSON representation + * of the entire global storage (this may be structured, for instance + * if maps or lists have been inserted). * * No tidying, sanitization, or censoring is done -- for instance, * the user module sets a slightly-obscured password in global storage, * and this JSON file will contain that password in-the-only-slightly- * obscured form. */ - bool save( const QString& filename ); + bool saveJson( const QString& filename ) const; /** @brief Adds the keys from the given JSON file * * No tidying, sanitization, or censoring is done. * The JSON file is read and each key is added as a value to - * the global storage. + * the global storage. The storage is not cleared first: existing + * keys will remain; keys that also occur in the JSON file are overwritten. */ - bool load( const QString& filename ); + bool loadJson( const QString& filename ); /** @brief write as YAML to the given filename * * See also save(), above. */ - bool saveYaml( const QString& filename ); + bool saveYaml( const QString& filename ) const; - /// @brief reads settings from the given filename + /** @brief reads settings from the given filename + * + * See also load(), above. + */ bool loadYaml( const QString& filename ); - /** @brief Get internal mapping as a constant object + /** @brief Make a complete copy of the data * - * Note that the VariantMap underneath may change, because - * it's not constant in itself. Connect to the changed() - * signal for notifications. + * Provides a snapshot of the data at a given time. */ - const QVariantMap& data() const { return m; } + QVariantMap data() const { return m; } public Q_SLOTS: + /** @brief Does the store contain the given key? + * + * This can distinguish an explicitly-inserted QVariant() from + * a no-value-exists QVariant. See value() for details. + */ bool contains( const QString& key ) const; + /** @brief The number of keys in the store + * + * This should be unsigned, but the underlying QMap uses signed as well. + * Equal to keys().length(), in theory. + */ int count() const; + /** @brief The keys in the store + * + * This makes a copy of all the keys currently in the store, which + * could be used for iterating over all the values in the store. + */ QStringList keys() const; + /** @brief Gets a value from the store + * + * If a value has been previously inserted, returns that value. + * If @p key does not exist in the store, returns a QVariant() + * (an invalid QVariant, which boolean-converts to false). Since + * QVariant() van also be inserted explicitly, use contains() + * to check for the presence of a key if you need that. + */ QVariant value( const QString& key ) const; signals: + /** @brief Emitted any time the store changes + * + * Also emitted sometimes when the store does not change, e.g. + * when removing a non-existent key or inserting a value that + * is already present. + */ void changed(); private: + class ReadLock; + class WriteLock; QVariantMap m; + mutable QMutex m_mutex; }; } // namespace Calamares diff --git a/src/libcalamares/Tests.cpp b/src/libcalamares/Tests.cpp new file mode 100644 index 000000000..53bedc544 --- /dev/null +++ b/src/libcalamares/Tests.cpp @@ -0,0 +1,185 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + * + */ + +#include "GlobalStorage.h" + +#include "utils/Logger.h" + +#include +#include +#include + +class TestLibCalamares : public QObject +{ + Q_OBJECT +public: + TestLibCalamares() {} + virtual ~TestLibCalamares() {} + +private Q_SLOTS: + void testGSModify(); + void testGSLoadSave(); + void testGSLoadSave2(); + void testGSLoadSaveYAMLStringList(); +}; + +void +TestLibCalamares::testGSModify() +{ + Calamares::GlobalStorage gs; + QSignalSpy spy( &gs, &Calamares::GlobalStorage::changed ); + + const QString key( "derp" ); + + QCOMPARE( gs.count(), 0 ); + QVERIFY( !gs.contains( key ) ); + + const int value = 17; + gs.insert( key, value ); + QCOMPARE( gs.count(), 1 ); + QVERIFY( gs.contains( key ) ); + QCOMPARE( gs.value( key ).type(), QVariant::Int ); + QCOMPARE( gs.value( key ).toString(), QString( "17" ) ); // It isn't a string, but does convert + QCOMPARE( gs.value( key ).toInt(), value ); + + gs.remove( key ); + QCOMPARE( gs.count(), 0 ); + QVERIFY( !gs.contains( key ) ); + + QCOMPARE( spy.count(), 2 ); // one insert, one remove +} + +void +TestLibCalamares::testGSLoadSave() +{ + Calamares::GlobalStorage gs; + const QString jsonfilename( "gs.test.json" ); + const QString yamlfilename( "gs.test.yaml" ); + + gs.insert( "derp", 17 ); + gs.insert( "cow", "moo" ); + + QVariantList l; + for ( const auto& s : QStringList { "dopey", "sneezy" } ) + { + l.append( s ); + } + gs.insert( "dwarfs", l ); + + QCOMPARE( gs.count(), 3 ); + + QVERIFY( gs.saveJson( jsonfilename ) ); + Calamares::GlobalStorage gs2; + QCOMPARE( gs2.count(), 0 ); + QVERIFY( gs2.loadJson( jsonfilename ) ); + QCOMPARE( gs2.count(), 3 ); + QCOMPARE( gs2.data(), gs.data() ); + + QVERIFY( gs.saveYaml( yamlfilename ) ); + Calamares::GlobalStorage gs3; + QCOMPARE( gs3.count(), 0 ); + QVERIFY( gs3.loadYaml( jsonfilename ) ); + QCOMPARE( gs3.count(), 3 ); + QCOMPARE( gs3.data(), gs.data() ); + + // YAML can load as JSON! + QVERIFY( gs3.loadYaml( jsonfilename ) ); + QCOMPARE( gs3.count(), 3 ); + QCOMPARE( gs3.data(), gs.data() ); + + // Failures in loading + QVERIFY( !gs3.loadJson( yamlfilename ) ); + + Calamares::GlobalStorage gs4; + gs4.insert( "derp", 32 ); + gs4.insert( "dorp", "Varsseveld" ); + QCOMPARE( gs4.count(), 2 ); + QVERIFY( gs4.contains( "dorp" ) ); + QCOMPARE( gs4.value( "derp" ).toInt(), 32 ); + QVERIFY( gs4.loadJson( jsonfilename ) ); + // 3 keys from the file, but one overwrite + QCOMPARE( gs4.count(), 4 ); + QVERIFY( gs4.contains( "dorp" ) ); + QCOMPARE( gs4.value( "derp" ).toInt(), 17 ); // This one was overwritten +} + +void +TestLibCalamares::testGSLoadSave2() +{ + Logger::setupLogLevel( Logger::LOGDEBUG ); + + const QString filename( "../src/libcalamares/testdata/yaml-list.conf" ); + if ( !QFile::exists( filename ) ) + { + return; + } + + Calamares::GlobalStorage gs1; + const QString key( "dwarfs" ); + + QVERIFY( gs1.loadYaml( filename ) ); + QCOMPARE( gs1.count(), 3 ); // From examining the file + QVERIFY( gs1.contains( key ) ); + cDebug() << gs1.value( key ).type() << gs1.value( key ); + QCOMPARE( gs1.value( key ).type(), QVariant::List ); + + const QString yamlfilename( "gs.test.yaml" ); + QVERIFY( gs1.saveYaml( yamlfilename ) ); + + Calamares::GlobalStorage gs2; + QVERIFY( gs2.loadYaml( yamlfilename ) ); + QVERIFY( gs2.contains( key ) ); + QCOMPARE( gs2.value( key ).type(), QVariant::List ); +} + +void +TestLibCalamares::testGSLoadSaveYAMLStringList() +{ + Calamares::GlobalStorage gs; + const QString yamlfilename( "gs.test.yaml" ); + + gs.insert( "derp", 17 ); + gs.insert( "cow", "moo" ); + gs.insert( "dwarfs", QStringList { "happy", "dopey", "sleepy", "sneezy", "doc", "thorin", "balin" } ); + + QCOMPARE( gs.count(), 3 ); + QCOMPARE( gs.value( "dwarfs" ).toList().count(), 7 ); // There's seven dwarfs, right? + QVERIFY( gs.value( "dwarfs" ).toStringList().contains( "thorin" ) ); + QVERIFY( !gs.value( "dwarfs" ).toStringList().contains( "gimli" ) ); + + + QVERIFY( gs.saveYaml( yamlfilename ) ); + + Calamares::GlobalStorage gs2; + QCOMPARE( gs2.count(), 0 ); + QVERIFY( gs2.loadYaml( yamlfilename ) ); + QCOMPARE( gs2.count(), 3 ); + QEXPECT_FAIL( "", "QStringList doesn't write out nicely", Continue ); + QCOMPARE( gs2.value( "dwarfs" ).toList().count(), 7 ); // There's seven dwarfs, right? + QCOMPARE( gs2.value( "dwarfs" ).toString(), QStringLiteral( "" ) ); // .. they're gone +} + + +QTEST_GUILESS_MAIN( TestLibCalamares ) + +#include "utils/moc-warnings.h" + +#include "Tests.moc" diff --git a/src/libcalamares/geoip/Interface.cpp b/src/libcalamares/geoip/Interface.cpp index f8649f37a..47c826e1a 100644 --- a/src/libcalamares/geoip/Interface.cpp +++ b/src/libcalamares/geoip/Interface.cpp @@ -47,7 +47,6 @@ splitTZString( const QString& tz ) QStringList tzParts = timezoneString.split( '/', SplitSkipEmptyParts ); if ( tzParts.size() >= 2 ) { - cDebug() << "GeoIP reporting" << timezoneString; QString region = tzParts.takeFirst(); QString zone = tzParts.join( '/' ); return RegionZonePair( region, zone ); diff --git a/src/libcalamares/locale/CountryData_p.cpp b/src/libcalamares/locale/CountryData_p.cpp index 4382950ec..722ee2ba9 100644 --- a/src/libcalamares/locale/CountryData_p.cpp +++ b/src/libcalamares/locale/CountryData_p.cpp @@ -2,7 +2,13 @@ * * === This file is part of Calamares - === * -* This file is derived from CLDR data from Unicode, Inc. Applicable terms: +* SPDX-FileCopyrightText: 1991-2019 Unicode, Inc. +* SPDX-FileCopyrightText: 2019 Adriaan de Groot +* SPDX-License-Identifier: CC0 +* +* This file is derived from CLDR data from Unicode, Inc. Applicable terms +* are listed at http://unicode.org/copyright.html , of which the most +* important are: * * A. Unicode Copyright * 1. Copyright © 1991-2019 Unicode, Inc. All rights reserved. @@ -10,6 +16,11 @@ * Unicode Data Files ("DATA FILES") include all data files under the directories: * https://www.unicode.org/Public/ * C. Terms of Use +* 1. Certain documents and files on this website contain a legend indicating +* that "Modification is permitted." Any person is hereby authorized, +* without fee, to modify such documents and files to create derivative +* works conforming to the Unicode® Standard, subject to Terms and +* Conditions herein. * 2. Any person is hereby authorized, without fee, to view, use, reproduce, * and distribute all documents and files, subject to the Terms and * Conditions herein. diff --git a/src/libcalamares/locale/Label.cpp b/src/libcalamares/locale/Label.cpp index 0be82a380..9b16bc757 100644 --- a/src/libcalamares/locale/Label.cpp +++ b/src/libcalamares/locale/Label.cpp @@ -1,7 +1,8 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +17,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "Label.h" diff --git a/src/libcalamares/locale/Label.h b/src/libcalamares/locale/Label.h index fa07e3361..f52e401e3 100644 --- a/src/libcalamares/locale/Label.h +++ b/src/libcalamares/locale/Label.h @@ -1,7 +1,8 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +17,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #ifndef LOCALE_LABEL_H diff --git a/src/libcalamares/locale/LabelModel.cpp b/src/libcalamares/locale/LabelModel.cpp index a7f14bf50..26adacfb8 100644 --- a/src/libcalamares/locale/LabelModel.cpp +++ b/src/libcalamares/locale/LabelModel.cpp @@ -1,7 +1,8 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Camilo Higuita * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +17,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "LabelModel.h" diff --git a/src/libcalamares/locale/LabelModel.h b/src/libcalamares/locale/LabelModel.h index 000bf4da7..36596c6c4 100644 --- a/src/libcalamares/locale/LabelModel.h +++ b/src/libcalamares/locale/LabelModel.h @@ -1,7 +1,8 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Camilo Higuita * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +17,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #ifndef LOCALE_LABELMODEL_H diff --git a/src/libcalamares/locale/Lookup.cpp b/src/libcalamares/locale/Lookup.cpp index 615783a87..b4ebe8f67 100644 --- a/src/libcalamares/locale/Lookup.cpp +++ b/src/libcalamares/locale/Lookup.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,9 +16,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "Lookup.h" diff --git a/src/libcalamares/locale/Lookup.h b/src/libcalamares/locale/Lookup.h index daa9c2987..4eae7c6b6 100644 --- a/src/libcalamares/locale/Lookup.h +++ b/src/libcalamares/locale/Lookup.h @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,9 +16,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #ifndef LOCALE_LOOKUP_H diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 6c5a508d7..6414e2ebf 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,13 +16,8 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ -#include "Tests.h" - #include "locale/LabelModel.h" #include "locale/TimeZone.h" #include "locale/TranslatableConfiguration.h" @@ -31,7 +27,35 @@ #include -QTEST_GUILESS_MAIN( LocaleTests ) +class LocaleTests : public QObject +{ + Q_OBJECT +public: + LocaleTests(); + ~LocaleTests() override; + +private Q_SLOTS: + void initTestCase(); + + void testLanguageModelCount(); + void testTranslatableLanguages(); + void testTranslatableConfig1(); + void testTranslatableConfig2(); + void testLanguageScripts(); + + void testEsperanto(); + void testInterlingue(); + + // TimeZone testing + void testRegions(); + void testSimpleZones(); + void testComplexZones(); + void testTZLookup(); + void testTZIterator(); + void testLocationLookup_data(); + void testLocationLookup(); + void testLocationLookup2(); +}; LocaleTests::LocaleTests() {} @@ -40,6 +64,8 @@ LocaleTests::~LocaleTests() {} void LocaleTests::initTestCase() { + Logger::setupLogLevel( Logger::LOGDEBUG ); + // Otherwise plain get() is dubious in the TranslatableConfiguration tests QLocale::setDefault( QLocale( QStringLiteral( "en_US" ) ) ); QVERIFY( ( QLocale().name() == "C" ) || ( QLocale().name() == "en_US" ) ); @@ -65,10 +91,8 @@ LocaleTests::testLanguageModelCount() } void -LocaleTests::testEsperanto() +LocaleTests::testLanguageScripts() { - Logger::setupLogLevel( Logger::LOGDEBUG ); - const auto* m = CalamaresUtils::Locale::availableTranslations(); QVERIFY( m ); @@ -89,13 +113,34 @@ LocaleTests::testEsperanto() QVERIFY( locale.language() == QLocale::Lithuanian ? locale.country() == QLocale::Lithuania : true ); QVERIFY( locale.language() != QLocale::C ); } +} + +void +LocaleTests::testEsperanto() +{ #if QT_VERSION < QT_VERSION_CHECK( 5, 12, 2 ) QCOMPARE( QLocale( "eo" ).language(), QLocale::C ); + QCOMPARE( QLocale( QLocale::Esperanto ).language(), QLocale::English ); #else QCOMPARE( QLocale( "eo" ).language(), QLocale::Esperanto ); + QCOMPARE( QLocale( QLocale::Esperanto ).language(), QLocale::Esperanto ); // Probably fails on 5.12, too #endif } +void +LocaleTests::testInterlingue() +{ + // ie / Interlingue is borked (is "ie" even the right name?) + QCOMPARE( QLocale( "ie" ).language(), QLocale::C ); + QCOMPARE( QLocale( QLocale::Interlingue ).language(), QLocale::English ); + + // "ia" exists (post-war variant of Interlingue) + QCOMPARE( QLocale( "ia" ).language(), QLocale::Interlingua ); + // "bork" does not exist + QCOMPARE( QLocale( "bork" ).language(), QLocale::C ); +} + + static const QStringList& someLanguages() { @@ -206,54 +251,214 @@ LocaleTests::testTranslatableConfig2() QCOMPARE( ts3.count(), 1 ); // The empty string } +void +LocaleTests::testRegions() +{ + using namespace CalamaresUtils::Locale; + RegionsModel regions; + + QVERIFY( regions.rowCount( QModelIndex() ) > 3 ); // Africa, America, Asia + + QStringList names; + for ( int i = 0; i < regions.rowCount( QModelIndex() ); ++i ) + { + QVariant name = regions.data( regions.index( i ), RegionsModel::NameRole ); + QVERIFY( name.isValid() ); + QVERIFY( !name.toString().isEmpty() ); + names.append( name.toString() ); + } + + QVERIFY( names.contains( "America" ) ); + QVERIFY( !names.contains( "UTC" ) ); +} + + +static void +displayedNames( QAbstractItemModel& model, QStringList& names ) +{ + names.clear(); + for ( int i = 0; i < model.rowCount( QModelIndex() ); ++i ) + { + QVariant name = model.data( model.index( i, 0 ), Qt::DisplayRole ); + QVERIFY( name.isValid() ); + QVERIFY( !name.toString().isEmpty() ); + names.append( name.toString() ); + } +} + void LocaleTests::testSimpleZones() { using namespace CalamaresUtils::Locale; + ZonesModel zones; + QVERIFY( zones.rowCount( QModelIndex() ) > 24 ); + + QStringList names; + displayedNames( zones, names ); + QVERIFY( names.contains( "Amsterdam" ) ); + if ( !names.contains( "New York" ) ) { - TZRegion r; - QVERIFY( r.tr().isEmpty() ); - } - { - TZZone n; - QVERIFY( n.tr().isEmpty() ); - } - { - TZZone r0( "xAmsterdam" ); - QCOMPARE( r0.tr(), QStringLiteral( "xAmsterdam" ) ); - TZZone r1( r0 ); - QCOMPARE( r0.tr(), QStringLiteral( "xAmsterdam" ) ); - QCOMPARE( r1.tr(), QStringLiteral( "xAmsterdam" ) ); - TZZone r2( std::move( r0 ) ); - QCOMPARE( r2.tr(), QStringLiteral( "xAmsterdam" ) ); - QCOMPARE( r0.tr(), QString() ); - } - { - TZZone r0( nullptr ); - QVERIFY( r0.tr().isEmpty() ); - TZZone r1( r0 ); - QVERIFY( r1.tr().isEmpty() ); - TZZone r2( std::move( r0 ) ); - QVERIFY( r2.tr().isEmpty() ); + for ( const auto& s : names ) + { + if ( s.startsWith( 'N' ) ) + { + cDebug() << s; + } + } } + QVERIFY( names.contains( "New York" ) ); + QVERIFY( !names.contains( "America" ) ); + QVERIFY( !names.contains( "New_York" ) ); } void LocaleTests::testComplexZones() { using namespace CalamaresUtils::Locale; + ZonesModel zones; + RegionalZonesModel europe( &zones ); - { - TZZone r0( "America/New_York" ); - TZZone r1( "America/New York" ); + QStringList names; + displayedNames( zones, names ); + QVERIFY( names.contains( "New York" ) ); + QVERIFY( names.contains( "Prague" ) ); + QVERIFY( names.contains( "Abidjan" ) ); - QCOMPARE( r0.tr(), r1.tr() ); - QCOMPARE( r0.tr(), QStringLiteral( "America/New York" ) ); - } - { - TZZone r( "zxc,;*_vm" ); - QVERIFY( !r.tr().isEmpty() ); - QCOMPARE( r.tr(), QStringLiteral( "zxc,;* vm" ) ); // Only _ is special - } + // No region set + displayedNames( europe, names ); + QVERIFY( names.contains( "New York" ) ); + QVERIFY( names.contains( "Prague" ) ); + QVERIFY( names.contains( "Abidjan" ) ); + + // Now filter + europe.setRegion( "Europe" ); + displayedNames( europe, names ); + QVERIFY( !names.contains( "New York" ) ); + QVERIFY( names.contains( "Prague" ) ); + QVERIFY( !names.contains( "Abidjan" ) ); + + europe.setRegion( "America" ); + displayedNames( europe, names ); + QVERIFY( names.contains( "New York" ) ); + QVERIFY( !names.contains( "Prague" ) ); + QVERIFY( !names.contains( "Abidjan" ) ); + + europe.setRegion( "Africa" ); + displayedNames( europe, names ); + QVERIFY( !names.contains( "New York" ) ); + QVERIFY( !names.contains( "Prague" ) ); + QVERIFY( names.contains( "Abidjan" ) ); } + +void +LocaleTests::testTZLookup() +{ + using namespace CalamaresUtils::Locale; + ZonesModel zones; + + QVERIFY( zones.find( "America", "New_York" ) ); + QCOMPARE( zones.find( "America", "New_York" )->zone(), QStringLiteral( "New_York" ) ); + QCOMPARE( zones.find( "America", "New_York" )->tr(), QStringLiteral( "New York" ) ); + + QVERIFY( !zones.find( "Europe", "New_York" ) ); + QVERIFY( !zones.find( "America", "New York" ) ); +} + +void +LocaleTests::testTZIterator() +{ + using namespace CalamaresUtils::Locale; + const ZonesModel zones; + + QVERIFY( zones.find( "Europe", "Rome" ) ); + + int count = 0; + bool seenRome = false; + bool seenGnome = false; + for ( auto it = zones.begin(); it; ++it ) + { + QVERIFY( *it ); + QVERIFY( !( *it )->zone().isEmpty() ); + seenRome |= ( *it )->zone() == QStringLiteral( "Rome" ); + seenGnome |= ( *it )->zone() == QStringLiteral( "Gnome" ); + count++; + } + + QVERIFY( seenRome ); + QVERIFY( !seenGnome ); + QCOMPARE( count, zones.rowCount( QModelIndex() ) ); + + QCOMPARE( zones.data( zones.index( 0 ), ZonesModel::RegionRole ).toString(), QStringLiteral( "Africa" ) ); + QCOMPARE( ( *zones.begin() )->zone(), QStringLiteral( "Abidjan" ) ); +} + +void +LocaleTests::testLocationLookup_data() +{ + QTest::addColumn< double >( "latitude" ); + QTest::addColumn< double >( "longitude" ); + QTest::addColumn< QString >( "name" ); + + QTest::newRow( "London" ) << 50.0 << 0.0 << QString( "London" ); + QTest::newRow( "Tarawa E" ) << 0.0 << 179.0 << QString( "Tarawa" ); + QTest::newRow( "Tarawa W" ) << 0.0 << -179.0 << QString( "Tarawa" ); + + QTest::newRow( "Johannesburg" ) << -26.0 << 28.0 << QString( "Johannesburg" ); // South Africa + QTest::newRow( "Maseru" ) << -29.0 << 27.0 << QString( "Maseru" ); // Lesotho + QTest::newRow( "Windhoek" ) << -22.0 << 17.0 << QString( "Windhoek" ); // Namibia + QTest::newRow( "Port Elisabeth" ) << -33.0 << 25.0 << QString( "Johannesburg" ); // South Africa + QTest::newRow( "Cape Town" ) << -33.0 << 18.0 << QString( "Johannesburg" ); // South Africa +} + +void +LocaleTests::testLocationLookup() +{ + const CalamaresUtils::Locale::ZonesModel zones; + + QFETCH( double, latitude ); + QFETCH( double, longitude ); + QFETCH( QString, name ); + + const auto* zone = zones.find( latitude, longitude ); + QVERIFY( zone ); + QCOMPARE( zone->zone(), name ); +} + +void +LocaleTests::testLocationLookup2() +{ + // Official + // ZA -2615+02800 Africa/Johannesburg + // Spot patch + // "ZA -3230+02259 Africa/Johannesburg\n"; + + const CalamaresUtils::Locale::ZonesModel zones; + const auto* zone = zones.find( -26.15, 28.00 ); + QCOMPARE( zone->zone(), QString( "Johannesburg" ) ); + // The TZ data sources use minutes-and-seconds notation, + // so "2615" is 26 degrees, 15 minutes, and 15 minutes is + // one-quarter of a degree. + QCOMPARE( zone->latitude(), -26.25 ); + QCOMPARE( zone->longitude(), 28.00 ); + + // Elsewhere in South Africa + const auto* altzone = zones.find( -32.0, 22.0 ); + QCOMPARE( altzone, zone ); // same pointer + QCOMPARE( altzone->zone(), QString( "Johannesburg" ) ); + QCOMPARE( altzone->latitude(), -26.25 ); + QCOMPARE( altzone->longitude(), 28.00 ); + + altzone = zones.find( -29.0, 27.0 ); + QCOMPARE( altzone->zone(), QString( "Maseru" ) ); + // -2928, that's -29 and 28/60 of a degree, is almost half, but we don't want + // to fall foul of variations in double-precision + QCOMPARE( trunc( altzone->latitude() * 1000.0 ), -29466 ); +} + + +QTEST_GUILESS_MAIN( LocaleTests ) + +#include "utils/moc-warnings.h" + +#include "Tests.moc" diff --git a/src/libcalamares/locale/Tests.h b/src/libcalamares/locale/Tests.h deleted file mode 100644 index dfe85a865..000000000 --- a/src/libcalamares/locale/Tests.h +++ /dev/null @@ -1,49 +0,0 @@ -/* === This file is part of Calamares - === - * - * SPDX-FileCopyrightText: 2019 Adriaan de Groot - * - * Calamares 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. - * - * Calamares 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 Calamares. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * - */ - -#ifndef LIBCALAMARES_LOCALE_TESTS_H -#define LIBCALAMARES_LOCALE_TESTS_H - -#include - -class LocaleTests : public QObject -{ - Q_OBJECT -public: - LocaleTests(); - ~LocaleTests() override; - -private Q_SLOTS: - void initTestCase(); - - void testLanguageModelCount(); - void testEsperanto(); - void testTranslatableLanguages(); - void testTranslatableConfig1(); - void testTranslatableConfig2(); - - // TimeZone testing - void testSimpleZones(); - void testComplexZones(); -}; - -#endif diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 772a242fb..3b3b99dce 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,24 +16,32 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "TimeZone.h" +#include "locale/TranslatableString.h" #include "utils/Logger.h" #include "utils/String.h" #include -#include -#include - -#include +#include static const char TZ_DATA_FILE[] = "/usr/share/zoneinfo/zone.tab"; +namespace CalamaresUtils +{ +namespace Locale +{ +class RegionData; +using RegionVector = QVector< RegionData* >; +using ZoneVector = QVector< TimeZoneData* >; + +/** @brief Turns a string longitude or latitude notation into a double + * + * This handles strings like "+4230+00131" from zone.tab, + * which is degrees-and-minutes notation, and + means north or east. + */ static double getRightGeoLocation( QString str ) { @@ -62,99 +71,45 @@ getRightGeoLocation( QString str ) } -namespace CalamaresUtils +TimeZoneData::TimeZoneData( const QString& region, + const QString& zone, + const QString& country, + double latitude, + double longitude ) + : TranslatableString( zone ) + , m_region( region ) + , m_country( country ) + , m_latitude( latitude ) + , m_longitude( longitude ) { -namespace Locale -{ - - -CStringPair::CStringPair( CStringPair&& t ) - : m_human( nullptr ) - , m_key() -{ - // My pointers are initialized to nullptr - std::swap( m_human, t.m_human ); - std::swap( m_key, t.m_key ); + setObjectName( region + '/' + zone ); } -CStringPair::CStringPair( const CStringPair& t ) - : m_human( t.m_human ? strdup( t.m_human ) : nullptr ) - , m_key( t.m_key ) -{ -} - -/** @brief Massage an identifier into a human-readable form - * - * Makes a copy of @p s, caller must free() it. - */ -static char* -munge( const char* s ) -{ - char* t = strdup( s ); - if ( !t ) - { - return nullptr; - } - - // replace("_"," ") in the Python script - char* p = t; - while ( *p ) - { - if ( ( *p ) == '_' ) - { - *p = ' '; - } - ++p; - } - - return t; -} - -CStringPair::CStringPair( const char* s1 ) - : m_human( s1 ? munge( s1 ) : nullptr ) - , m_key( s1 ? QString( s1 ) : QString() ) -{ -} - - -CStringPair::~CStringPair() -{ - free( m_human ); -} - - QString -TZRegion::tr() const +TimeZoneData::tr() const +{ + // NOTE: context name must match what's used in zone-extractor.py + return QObject::tr( m_human, "tz_names" ); +} + + +class RegionData : public TranslatableString +{ +public: + using TranslatableString::TranslatableString; + QString tr() const override; +}; + +QString +RegionData::tr() const { // NOTE: context name must match what's used in zone-extractor.py return QObject::tr( m_human, "tz_regions" ); } -TZRegion::~TZRegion() +static void +loadTZData( RegionVector& regions, ZoneVector& zones, QTextStream& in ) { - qDeleteAll( m_zones ); -} - -const CStringPairList& -TZRegion::fromZoneTab() -{ - static CStringPairList zoneTab = TZRegion::fromFile( TZ_DATA_FILE ); - return zoneTab; -} - -CStringPairList -TZRegion::fromFile( const char* fileName ) -{ - CStringPairList model; - - QFile file( fileName ); - if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) - { - return model; - } - - TZRegion* thisRegion = nullptr; - QTextStream in( &file ); while ( !in.atEnd() ) { QString line = in.readLine().trimmed().split( '#', SplitKeepEmptyParts ).first().trimmed(); @@ -181,18 +136,6 @@ TZRegion::fromFile( const char* fileName ) continue; } - auto keyMatch = [®ion]( const CStringPair* r ) { return r->key() == region; }; - auto it = std::find_if( model.begin(), model.end(), keyMatch ); - if ( it != model.end() ) - { - thisRegion = dynamic_cast< TZRegion* >( *it ); - } - else - { - thisRegion = new TZRegion( region.toUtf8().data() ); - model.append( thisRegion ); - } - QString countryCode = list.at( 0 ).trimmed(); if ( countryCode.size() != 2 ) { @@ -200,114 +143,365 @@ TZRegion::fromFile( const char* fileName ) } timezoneParts.removeFirst(); - thisRegion->m_zones.append( - new TZZone( region, timezoneParts.join( '/' ).toUtf8().constData(), countryCode, list.at( 1 ) ) ); - } - - auto sorter = []( const CStringPair* l, const CStringPair* r ) { return *l < *r; }; - std::sort( model.begin(), model.end(), sorter ); - for ( auto& it : model ) - { - TZRegion* r = dynamic_cast< TZRegion* >( it ); - if ( r ) + QString zone = timezoneParts.join( '/' ); + if ( zone.length() < 2 ) { - std::sort( r->m_zones.begin(), r->m_zones.end(), sorter ); + continue; + } + + QString position = list.at( 1 ); + int cooSplitPos = position.indexOf( QRegExp( "[-+]" ), 1 ); + double latitude; + double longitude; + if ( cooSplitPos > 0 ) + { + latitude = getRightGeoLocation( position.mid( 0, cooSplitPos ) ); + longitude = getRightGeoLocation( position.mid( cooSplitPos ) ); + } + else + { + continue; + } + + // Now we have region, zone, country, lat and longitude + const RegionData* existingRegion = nullptr; + for ( const auto* p : regions ) + { + if ( p->key() == region ) + { + existingRegion = p; + break; + } + } + if ( !existingRegion ) + { + regions.append( new RegionData( region ) ); + } + zones.append( new TimeZoneData( region, zone, countryCode, latitude, longitude ) ); + } +} + +/** @brief Extra, fake, timezones + * + * The timezone locations in zone.tab are not always very useful, + * given Calamares's standard "nearest zone" algorithm: for instance, + * in most locations physically in the country of South Africa, + * Maseru (the capital of Lesotho, and location for timezone Africa/Maseru) + * is closer than Johannesburg (the location for timezone Africa/Johannesburg). + * + * The algorithm picks the wrong place. This is for instance annoying + * when clicking on Cape Town, you get Maseru, and to get Johannesburg + * you need to click somewhere very carefully north of Maserru. + * + * These alternate zones are used to introduce "extra locations" + * into the timezone database, in order to influence the closest-location + * algorithm. Lines are formatted just like in zone.tab: remember the \n + */ +static const char altZones[] = + /* This extra zone is north-east of Karoo National park, + * and means that Western Cape province and a good chunk of + * Northern- and Eastern- Cape provinces get pulled in to Johannesburg. + * Bloemfontein is still closer to Maseru than either correct zone, + * but this is a definite improvement. + */ + "ZA -3230+02259 Africa/Johannesburg\n"; + +class Private : public QObject +{ + Q_OBJECT +public: + RegionVector m_regions; + ZoneVector m_zones; + ZoneVector m_altZones; //< Extra locations for zones + + Private() + { + m_regions.reserve( 12 ); // reasonable guess + m_zones.reserve( 452 ); // wc -l /usr/share/zoneinfo/zone.tab + + // Load the official timezones + { + QFile file( TZ_DATA_FILE ); + if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + QTextStream in( &file ); + loadTZData( m_regions, m_zones, in ); + } + } + // Load the alternate zones (see documentation at altZones) + { + QTextStream in( altZones ); + loadTZData( m_regions, m_altZones, in ); + } + + std::sort( m_regions.begin(), m_regions.end(), []( const RegionData* lhs, const RegionData* rhs ) { + return lhs->key() < rhs->key(); + } ); + std::sort( m_zones.begin(), m_zones.end(), []( const TimeZoneData* lhs, const TimeZoneData* rhs ) { + if ( lhs->region() == rhs->region() ) + { + return lhs->zone() < rhs->zone(); + } + return lhs->region() < rhs->region(); + } ); + + for ( auto* z : m_zones ) + { + z->setParent( this ); } } +}; - return model; -} - -TZZone::TZZone( const QString& region, const char* zoneName, const QString& country, QString position ) - : CStringPair( zoneName ) - , m_region( region ) - , m_country( country ) +static Private* +privateInstance() { - int cooSplitPos = position.indexOf( QRegExp( "[-+]" ), 1 ); - if ( cooSplitPos > 0 ) - { - m_latitude = getRightGeoLocation( position.mid( 0, cooSplitPos ) ); - m_longitude = getRightGeoLocation( position.mid( cooSplitPos ) ); - } + static Private* s_p = new Private; + return s_p; } -QString -TZZone::tr() const -{ - // NOTE: context name must match what's used in zone-extractor.py - return QObject::tr( m_human, "tz_names" ); -} - - -CStringListModel::CStringListModel( CStringPairList l ) - : m_list( l ) +RegionsModel::RegionsModel( QObject* parent ) + : QAbstractListModel( parent ) + , m_private( privateInstance() ) { } -void -CStringListModel::setList( CalamaresUtils::Locale::CStringPairList l ) -{ - beginResetModel(); - m_list = l; - endResetModel(); -} +RegionsModel::~RegionsModel() {} int -CStringListModel::rowCount( const QModelIndex& ) const +RegionsModel::rowCount( const QModelIndex& ) const { - return m_list.count(); + return m_private->m_regions.count(); } QVariant -CStringListModel::data( const QModelIndex& index, int role ) const +RegionsModel::data( const QModelIndex& index, int role ) const { - if ( ( role != Qt::DisplayRole ) && ( role != Qt::UserRole ) ) + if ( !index.isValid() || index.row() < 0 || index.row() >= m_private->m_regions.count() ) { return QVariant(); } - if ( !index.isValid() ) + const auto& region = m_private->m_regions[ index.row() ]; + if ( role == NameRole ) { - return QVariant(); + return region->tr(); } - - const auto* item = m_list.at( index.row() ); - return item ? ( role == Qt::DisplayRole ? item->tr() : item->key() ) : QVariant(); -} - -void -CStringListModel::setCurrentIndex( int index ) -{ - if ( ( index < 0 ) || ( index >= m_list.count() ) ) + if ( role == KeyRole ) { - return; + return region->key(); } - - m_currentIndex = index; - emit currentIndexChanged(); -} - -int -CStringListModel::currentIndex() const -{ - return m_currentIndex; + return QVariant(); } QHash< int, QByteArray > -CStringListModel::roleNames() const +RegionsModel::roleNames() const { - return { { Qt::DisplayRole, "label" }, { Qt::UserRole, "key" } }; + return { { NameRole, "name" }, { KeyRole, "key" } }; } -const CStringPair* -CStringListModel::item( int index ) const +QString +RegionsModel::tr( const QString& region ) const { - if ( ( index < 0 ) || ( index >= m_list.count() ) ) + for ( const auto* p : m_private->m_regions ) { - return nullptr; + if ( p->key() == region ) + { + return p->tr(); + } } - return m_list[ index ]; + return region; } +ZonesModel::ZonesModel( QObject* parent ) + : QAbstractListModel( parent ) + , m_private( privateInstance() ) +{ +} + +ZonesModel::~ZonesModel() {} + +int +ZonesModel::rowCount( const QModelIndex& ) const +{ + return m_private->m_zones.count(); +} + +QVariant +ZonesModel::data( const QModelIndex& index, int role ) const +{ + if ( !index.isValid() || index.row() < 0 || index.row() >= m_private->m_zones.count() ) + { + return QVariant(); + } + + const auto* zone = m_private->m_zones[ index.row() ]; + switch ( role ) + { + case NameRole: + return zone->tr(); + case KeyRole: + return zone->key(); + case RegionRole: + return zone->region(); + default: + return QVariant(); + } +} + +QHash< int, QByteArray > +ZonesModel::roleNames() const +{ + return { { NameRole, "name" }, { KeyRole, "key" } }; +} + +const TimeZoneData* +ZonesModel::find( const QString& region, const QString& zone ) const +{ + for ( const auto* p : m_private->m_zones ) + { + if ( p->region() == region && p->zone() == zone ) + { + return p; + } + } + return nullptr; +} + +STATICTEST const TimeZoneData* +find( double startingDistance, const ZoneVector& zones, const std::function< double( const TimeZoneData* ) >& distanceFunc ) +{ + double smallestDistance = startingDistance; + const TimeZoneData* closest = nullptr; + + for ( const auto* zone : zones ) + { + double thisDistance = distanceFunc( zone ); + if ( thisDistance < smallestDistance ) + { + closest = zone; + smallestDistance = thisDistance; + } + } + return closest; +} + +const TimeZoneData* +ZonesModel::find( const std::function< double( const TimeZoneData* ) >& distanceFunc ) const +{ + const auto* officialZone = CalamaresUtils::Locale::find( 1000000.0, m_private->m_zones, distanceFunc ); + const auto* altZone = CalamaresUtils::Locale::find( distanceFunc( officialZone ), m_private->m_altZones, distanceFunc ); + + // If nothing was closer than the official zone already was, altZone is + // nullptr; but if there is a spot-patch, then we need to re-find + // the zone by name, since we want to always return pointers into + // m_zones, not into the alternative spots. + return altZone ? find( altZone->region(), altZone->zone() ) : officialZone; +} + +const TimeZoneData* +ZonesModel::find( double latitude, double longitude ) const +{ + /* This is a somewhat derpy way of finding "closest", + * in that it considers one degree of separation + * either N/S or E/W equal to any other; this obviously + * falls apart at the poles. + */ + auto distance = [&]( const TimeZoneData* zone ) -> double { + // Latitude doesn't wrap around: there is nothing north of 90 + double latitudeDifference = abs( zone->latitude() - latitude ); + + // Longitude **does** wrap around, so consider the case of -178 and 178 + // which differ by 4 degrees. + double westerly = qMin( zone->longitude(), longitude ); + double easterly = qMax( zone->longitude(), longitude ); + double longitudeDifference = 0.0; + if ( westerly < 0.0 && !( easterly < 0.0 ) ) + { + // Only if they're different signs can we have wrap-around. + longitudeDifference = qMin( abs( westerly - easterly ), abs( 360.0 + westerly - easterly ) ); + } + else + { + longitudeDifference = abs( westerly - easterly ); + } + + return latitudeDifference + longitudeDifference; + }; + + return find( distance ); +} + +QObject* +ZonesModel::lookup( double latitude, double longitude ) const +{ + const auto* p = find( latitude, longitude ); + if ( !p ) + { + p = find( "America", "New_York" ); + } + if ( !p ) + { + cWarning() << "No zone (not even New York) found, expect crashes."; + } + return const_cast< QObject* >( reinterpret_cast< const QObject* >( p ) ); +} + + +ZonesModel::Iterator::operator bool() const +{ + return 0 <= m_index && m_index < m_p->m_zones.count(); +} + +const TimeZoneData* ZonesModel::Iterator::operator*() const +{ + if ( *this ) + { + return m_p->m_zones[ m_index ]; + } + return nullptr; +} + +RegionalZonesModel::RegionalZonesModel( CalamaresUtils::Locale::ZonesModel* source, QObject* parent ) + : QSortFilterProxyModel( parent ) + , m_private( privateInstance() ) +{ + setSourceModel( source ); +} + +RegionalZonesModel::~RegionalZonesModel() {} + +void +RegionalZonesModel::setRegion( const QString& r ) +{ + if ( r != m_region ) + { + m_region = r; + invalidateFilter(); + emit regionChanged( r ); + } +} + +bool +RegionalZonesModel::filterAcceptsRow( int sourceRow, const QModelIndex& ) const +{ + if ( m_region.isEmpty() ) + { + return true; + } + + if ( sourceRow < 0 || sourceRow >= m_private->m_zones.count() ) + { + return false; + } + + const auto& zone = m_private->m_zones[ sourceRow ]; + return ( zone->m_region == m_region ); +} + + } // namespace Locale } // namespace CalamaresUtils + +#include "utils/moc-warnings.h" + +#include "TimeZone.moc" diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 05820817a..e14f33e36 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -16,192 +16,230 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * */ +/** @file Timezone data and models to go with it + * + * The TimeZoneData class holds information from zone.tab, about + * TZ names and locations (latitude and longitude) for geographic + * lookups. + * + * The RegionModel lists the regions of the world (about 12) and + * ZonesModel lists all the timezones; the RegionalZonesModel provides + * a way to restrict the view of timezones to those of a specific region. + * + */ #ifndef LOCALE_TIMEZONE_H #define LOCALE_TIMEZONE_H #include "DllMacro.h" -#include "utils/Logger.h" +#include "locale/TranslatableString.h" #include #include -#include - -#include +#include +#include namespace CalamaresUtils { namespace Locale { +class Private; +class RegionalZonesModel; +class ZonesModel; -/** @brief A pair of strings, one human-readable, one a key - * - * Given an identifier-like string (e.g. "New_York"), makes - * a human-readable version of that and keeps a copy of the - * identifier itself. - * - * This explicitly uses const char* instead of just being - * QPair because there is API that needs - * C-style strings. - */ -class CStringPair : public QObject +class TimeZoneData : public QObject, TranslatableString { + friend class RegionalZonesModel; + friend class ZonesModel; + Q_OBJECT + + Q_PROPERTY( QString region READ region CONSTANT ) + Q_PROPERTY( QString zone READ zone CONSTANT ) + Q_PROPERTY( QString name READ tr CONSTANT ) + Q_PROPERTY( QString countryCode READ country CONSTANT ) + public: - /// @brief An empty pair - CStringPair() {} - /// @brief Given an identifier, create the pair - explicit CStringPair( const char* s1 ); - CStringPair( CStringPair&& t ); - CStringPair( const CStringPair& ); - virtual ~CStringPair(); + TimeZoneData( const QString& region, + const QString& zone, + const QString& country, + double latitude, + double longitude ); + TimeZoneData( const TimeZoneData& ) = delete; + TimeZoneData( TimeZoneData&& ) = delete; - /// @brief Give the localized human-readable form - virtual QString tr() const = 0; - QString key() const { return m_key; } - - bool operator<( const CStringPair& other ) const { return m_key < other.m_key; } - -protected: - char* m_human = nullptr; - QString m_key; -}; - -class CStringPairList : public QList< CStringPair* > -{ -public: - template < typename T > - T* find( const QString& key ) const - { - for ( auto* p : *this ) - { - if ( p->key() == key ) - { - return dynamic_cast< T* >( p ); - } - } - return nullptr; - } -}; - -/** @brief Timezone regions (e.g. "America") - * - * A region has a key and a human-readable name, but also - * a collection of associated timezone zones (TZZone, below). - * This class is not usually constructed, but uses fromFile() - * to load a complete tree structure of timezones. - */ -class TZRegion : public CStringPair -{ - Q_OBJECT -public: - using CStringPair::CStringPair; - virtual ~TZRegion() override; - TZRegion( const TZRegion& ) = delete; QString tr() const override; - QString region() const { return key(); } - - /** @brief Create list from a zone.tab-like file - * - * Returns a list of all the regions; each region has a list - * of zones within that region. Dyamically, the items in the - * returned list are TZRegions; their zones dynamically are - * TZZones even though all those lists have type CStringPairList. - * - * The list owns the regions, and the regions own their own list of zones. - * When getting rid of the list, remember to qDeleteAll() on it. - */ - static CStringPairList fromFile( const char* fileName ); - /// @brief Calls fromFile with the standard zone.tab name - static const CStringPairList& fromZoneTab(); - - const CStringPairList& zones() const { return m_zones; } - -private: - CStringPairList m_zones; -}; - -/** @brief Specific timezone zones (e.g. "New_York", "New York") - * - * A timezone zone lives in a region, and has some associated - * data like the country (used to map likely languages) and latitude - * and longitude information. - */ -class TZZone : public CStringPair -{ - Q_OBJECT -public: - using CStringPair::CStringPair; - QString tr() const override; - - TZZone( const QString& region, const char* zoneName, const QString& country, QString position ); - QString region() const { return m_region; } QString zone() const { return key(); } + QString country() const { return m_country; } double latitude() const { return m_latitude; } double longitude() const { return m_longitude; } -protected: +private: QString m_region; QString m_country; - double m_latitude = 0.0, m_longitude = 0.0; + double m_latitude; + double m_longitude; }; -class CStringListModel : public QAbstractListModel + +/** @brief The list of timezone regions + * + * The regions are a short list of global areas (Africa, America, India ..) + * which contain zones. + */ +class DLLEXPORT RegionsModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY( int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged ) public: - /// @brief Create empty model - CStringListModel() {} - /// @brief Create model from list (non-owning) - CStringListModel( CStringPairList ); + enum Roles + { + NameRole = Qt::DisplayRole, + KeyRole = Qt::UserRole // So that currentData() will get the key + }; + + RegionsModel( QObject* parent = nullptr ); + virtual ~RegionsModel() override; int rowCount( const QModelIndex& parent ) const override; - QVariant data( const QModelIndex& index, int role ) const override; - const CStringPair* item( int index ) const; QHash< int, QByteArray > roleNames() const override; - void setCurrentIndex( int index ); - int currentIndex() const; - - void setList( CStringPairList ); - - inline int indexOf( const QString& key ) - { - const auto it = std::find_if( - m_list.constBegin(), m_list.constEnd(), [&]( const CalamaresUtils::Locale::CStringPair* item ) -> bool { - return item->key() == key; - } ); - - if ( it != m_list.constEnd() ) - { - // distance() is usually a long long - return int( std::distance( m_list.constBegin(), it ) ); - } - else - { - return -1; - } - } - +public Q_SLOTS: + /** @brief Provides a human-readable version of the region + * + * Returns @p region unchanged if there is no such region + * or no translation for the region's name. + */ + QString tr( const QString& region ) const; private: - CStringPairList m_list; - int m_currentIndex = -1; + Private* m_private; +}; + +class DLLEXPORT ZonesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles + { + NameRole = Qt::DisplayRole, + KeyRole = Qt::UserRole, // So that currentData() will get the key + RegionRole = Qt::UserRole + 1 + }; + + ZonesModel( QObject* parent = nullptr ); + virtual ~ZonesModel() override; + + int rowCount( const QModelIndex& parent ) const override; + QVariant data( const QModelIndex& index, int role ) const override; + + QHash< int, QByteArray > roleNames() const override; + + /** @brief Iterator for the underlying list of zones + * + * Iterates over all the zones in the model. Operator * may return + * a @c nullptr when the iterator is not valid. Typical usage: + * + * ``` + * for( auto it = model.begin(); it; ++it ) + * { + * const auto* zonedata = *it; + * ... + * } + */ + class Iterator + { + friend class ZonesModel; + Iterator( const Private* m ) + : m_index( 0 ) + , m_p( m ) + { + } + + public: + operator bool() const; + void operator++() { ++m_index; } + const TimeZoneData* operator*() const; + int index() const { return m_index; } + + private: + int m_index; + const Private* m_p; + }; + + Iterator begin() const { return Iterator( m_private ); } + + /** @brief Look up TZ data based on an arbitrary distance function + * + * This is a generic method that can define distance in whatever + * coordinate system is wanted; returns the zone with the smallest + * distance. The @p distanceFunc must return "the distance" for + * each zone. It would be polite to return something non-negative. + * + * Note: not a slot, because the parameter isn't moc-able. + */ + const TimeZoneData* find( const std::function< double( const TimeZoneData* ) >& distanceFunc ) const; + +public Q_SLOTS: + /** @brief Look up TZ data based on its name. + * + * Returns @c nullptr if not found. + */ + const TimeZoneData* find( const QString& region, const QString& zone ) const; + + /** @brief Look up TZ data based on the location. + * + * Returns the nearest zone to the given lat and lon. This is a + * convenience function for calling find(), below, with a standard + * distance function based on the distance between the given + * location (lat and lon) and each zone's given location. + */ + const TimeZoneData* find( double latitude, double longitude ) const; + + /** @brief Look up TZ data based on the location. + * + * Returns the nearest zone, or New York. This is non-const for QML + * purposes, but the object should be considered const anyway. + */ + QObject* lookup( double latitude, double longitude ) const; + +private: + Private* m_private; +}; + +class DLLEXPORT RegionalZonesModel : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY( QString region READ region WRITE setRegion NOTIFY regionChanged ) + +public: + RegionalZonesModel( ZonesModel* source, QObject* parent = nullptr ); + ~RegionalZonesModel() override; + + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override; + + QString region() const { return m_region; } + +public Q_SLOTS: + void setRegion( const QString& r ); signals: - void currentIndexChanged(); + void regionChanged( const QString& ); + +private: + Private* m_private; + QString m_region; }; + } // namespace Locale } // namespace CalamaresUtils diff --git a/src/libcalamares/locale/TranslatableConfiguration.cpp b/src/libcalamares/locale/TranslatableConfiguration.cpp index fb9800d64..618ecfa4f 100644 --- a/src/libcalamares/locale/TranslatableConfiguration.cpp +++ b/src/libcalamares/locale/TranslatableConfiguration.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,9 +16,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "TranslatableConfiguration.h" diff --git a/src/libcalamares/locale/TranslatableConfiguration.h b/src/libcalamares/locale/TranslatableConfiguration.h index fa6016731..a47f70eee 100644 --- a/src/libcalamares/locale/TranslatableConfiguration.h +++ b/src/libcalamares/locale/TranslatableConfiguration.h @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +16,16 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ +/** @file Run-time translation of strings from configuration files + * + * The TranslatedString class provides a way of doing run-time + * lookups of human-readable strings, from data provided in + * the configuration files (*.conf) for Calamares. This acts + * like "normal" translation through tr() calls, as far as the + * user-visible part goes. + */ #ifndef LOCALE_TRANSLATABLECONFIGURATION_H #define LOCALE_TRANSLATABLECONFIGURATION_H diff --git a/src/libcalamares/locale/TranslatableString.cpp b/src/libcalamares/locale/TranslatableString.cpp new file mode 100644 index 000000000..9200c8d65 --- /dev/null +++ b/src/libcalamares/locale/TranslatableString.cpp @@ -0,0 +1,89 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + * + */ +#include "TranslatableString.h" + + +/** @brief Massage an identifier into a human-readable form + * + * Makes a copy of @p s, caller must free() it. + */ +static char* +munge( const char* s ) +{ + char* t = strdup( s ); + if ( !t ) + { + return nullptr; + } + + // replace("_"," ") in the Python script + char* p = t; + while ( *p ) + { + if ( ( *p ) == '_' ) + { + *p = ' '; + } + ++p; + } + + return t; +} + +namespace CalamaresUtils +{ +namespace Locale +{ + +TranslatableString::TranslatableString( TranslatableString&& t ) + : m_human( nullptr ) + , m_key() +{ + // My pointers are initialized to nullptr + std::swap( m_human, t.m_human ); + std::swap( m_key, t.m_key ); +} + +TranslatableString::TranslatableString( const TranslatableString& t ) + : m_human( t.m_human ? strdup( t.m_human ) : nullptr ) + , m_key( t.m_key ) +{ +} + +TranslatableString::TranslatableString( const char* s1 ) + : m_human( s1 ? munge( s1 ) : nullptr ) + , m_key( s1 ? QString( s1 ) : QString() ) +{ +} + +TranslatableString::TranslatableString( const QString& s ) + : m_human( munge( s.toUtf8().constData() ) ) + , m_key( s ) +{ +} + + +TranslatableString::~TranslatableString() +{ + free( m_human ); +} + +} // namespace Locale +} // namespace CalamaresUtils diff --git a/src/libcalamares/locale/TranslatableString.h b/src/libcalamares/locale/TranslatableString.h new file mode 100644 index 000000000..8347488f1 --- /dev/null +++ b/src/libcalamares/locale/TranslatableString.h @@ -0,0 +1,68 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + * + */ +#ifndef LOCALE_TRANSLATABLESTRING_H +#define LOCALE_TRANSLATABLESTRING_H + +#include + +namespace CalamaresUtils +{ +namespace Locale +{ + +/** @brief A pair of strings, one human-readable, one a key + * + * Given an identifier-like string (e.g. "New_York"), makes + * a human-readable version of that and keeps a copy of the + * identifier itself. + * + * This explicitly uses const char* instead of just being + * QPair because the human-readable part + * may need to be translated through tr(), and that takes a char* + * C-style strings. + */ +class TranslatableString +{ +public: + /// @brief An empty pair + TranslatableString() {} + /// @brief Given an identifier, create the pair + explicit TranslatableString( const char* s1 ); + explicit TranslatableString( const QString& s ); + TranslatableString( TranslatableString&& t ); + TranslatableString( const TranslatableString& ); + virtual ~TranslatableString(); + + /// @brief Give the localized human-readable form + virtual QString tr() const = 0; + QString key() const { return m_key; } + + bool operator==( const TranslatableString& other ) const { return m_key == other.m_key; } + bool operator<( const TranslatableString& other ) const { return m_key < other.m_key; } + +protected: + char* m_human = nullptr; + QString m_key; +}; + +} // namespace Locale +} // namespace CalamaresUtils + +#endif diff --git a/src/libcalamares/locale/ZoneData_p.cxxtr b/src/libcalamares/locale/ZoneData_p.cxxtr index 4bfef3c0c..7bbcdf7f6 100644 --- a/src/libcalamares/locale/ZoneData_p.cxxtr +++ b/src/libcalamares/locale/ZoneData_p.cxxtr @@ -1,26 +1,8 @@ /* GENERATED FILE DO NOT EDIT * -* === This file is part of Calamares - === - * - * SPDX-FileCopyrightText: 2019 Adriaan de Groot - * - * Calamares 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. - * - * Calamares 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 Calamares. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * - * +* SPDX-FileCopyrightText: 2009 Arthur David Olson +* SPDX-FileCopyrightText: 2019 Adriaan de Groot +* SPDX-License-Identifier: CC0 * * This file is derived from zone.tab, which has its own copyright statement: * diff --git a/src/libcalamares/locale/cldr-extractor.py b/src/libcalamares/locale/cldr-extractor.py index f69407257..0291abf7d 100644 --- a/src/libcalamares/locale/cldr-extractor.py +++ b/src/libcalamares/locale/cldr-extractor.py @@ -1,45 +1,9 @@ #! /usr/bin/env python3 # # === This file is part of Calamares - === -# +# # SPDX-FileCopyrightText: 2019 Adriaan de Groot -# # SPDX-License-Identifier: BSD-2-Clause -# License-Filename: LICENSES/BSD2 -# -# -# -# Python3 script to scrape some data out of ICU CLDR supplemental data. -# -### BEGIN LICENSES -# -# Copyright 2019 Adriaan de Groot -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -### END LICENSES - -### BEGIN USAGE # """ Python3 script to scrape some data out of ICU CLDR supplemental data. @@ -126,7 +90,7 @@ class CountryData: self.country_code = "" self.language_enum = "AnyLanguage" self.country_enum = "AnyCountry" - + def __str__(self): if self.country_code: char0 = "'{!s}'".format(self.country_code[0]) @@ -134,18 +98,18 @@ class CountryData: else: char0 = "0" char1 = "0" - + return "{!s} QLocale::Language::{!s}, QLocale::Country::{!s}, {!s}, {!s} {!s},".format( "{", - self.language_enum, + self.language_enum, self.country_enum, - char0, + char0, char1, "}") # Must match type name below cpp_classname = "CountryData" - + # Must match the output format of __str__ above cpp_declaration = """ struct CountryData @@ -169,30 +133,30 @@ def extricate_subtags(l1, l2): return if '{ ?; ?;' not in l2: return - + # This is extremely crude "parsing" which chops up the string # by delimiter and then extracts some substring. l1_parts = l1.split("und_") l2_parts = l2.split(";") - + l1_first_quote = l1_parts[1].find('"') l1_code = l1_parts[1][:l1_first_quote] if len(l1_code) != 2: return - + l2_brace = l2_parts[2].find("{") l2_language = l2_parts[2][l2_brace+1:].strip() l2_brace = l2_parts[2].find("}") l2_country = l2_parts[2][:l2_brace-1].strip() - + # Handle mapped cases l2_language = language_mapper.get(l2_language, l2_language) l2_language = l2_language.replace(" ", "") - + # Handle mapped cases and then do a bunch of standard replacements. l2_country = country_mapper.get(l2_country, l2_country) l2_country = l2_country.replace(" ", "").replace("-", "").replace(".","").replace("&","And") - + return CountryData(l1_code, l2_language, l2_country) @@ -213,7 +177,7 @@ def read_subtags_file(): if l1: assert "likelySubtag" in l1, l1; assert "