Skip to content

Commit

Permalink
Issue #205: Add support for translated country names
Browse files Browse the repository at this point in the history
Activated with the SysConfig setting ReferenceData::TranslatedCountryNames.
Also show the flag in the country selection.
Load language packs for the most popular languages.
  • Loading branch information
bschmalhofer committed Dec 9, 2023
1 parent 16d44fe commit f4d2dff
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 45 deletions.
7 changes: 7 additions & 0 deletions Kernel/Config/Files/XML/Framework.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3098,6 +3098,13 @@ You can log in via the following URL:
</Hash>
</Value>
</Setting>
<Setting Name="ReferenceData::TranslatedCountryNames" Required="0" Valid="0">
<Description Translatable="1">Translate the country names in the country selection. The CLDR country codes will be stored. Needs Locale::CLDR and the relevant language packs.</Description>
<Navigation>Core::ReferenceData</Navigation>
<Value>
<Item ValueType="Checkbox">1</Item>
</Value>
</Setting>
<Setting Name="PerformanceLog" Required="0" Valid="1" ConfigLevel="200">
<Description Translatable="1">Enables performance log (to log the page response time). It will affect the system performance. Frontend::Module###AdminPerformanceLog must be enabled.</Description>
<Navigation>Core::PerformanceLog</Navigation>
Expand Down
38 changes: 22 additions & 16 deletions Kernel/Modules/AdminCustomerCompany.pm
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

package Kernel::Modules::AdminCustomerCompany;

use v5.24;
use strict;
use warnings;
use namespace::autoclean;

# core modules
use List::Util qw(any);
Expand Down Expand Up @@ -554,6 +556,7 @@ sub _Edit {
my ( $Self, %Param ) = @_;

my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');

$LayoutObject->Block(
Name => 'Overview',
Expand All @@ -571,9 +574,6 @@ sub _Edit {
Data => \%Param,
);

# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');

# send parameter ReadOnly to JS object
$LayoutObject->AddJSData(
Key => 'ReadOnly',
Expand Down Expand Up @@ -662,16 +662,24 @@ sub _Edit {
);

}
elsif ( $Entry->[0] =~ /^CustomerCompanyCountry/i ) {
my $OptionRequired = '';
if ( $Entry->[4] ) {
$OptionRequired = 'Validate_Required';
}

# build Country string
my $CountryList = $Kernel::OM->Get('Kernel::System::ReferenceData')->CountryList();
elsif ( $Entry->[0] =~ m/^CustomerCompanyCountry/i ) {

# build Country selection with English names
$Block = 'Option';
my $OptionRequired = $Entry->[4] ? 'Validate_Required' : '';
my $CountryList;
if ( $ConfigObject->Get('ReferenceData::TranslatedCountryNames') ) {

# Flag+Name => code
$CountryList = $Kernel::OM->Get('Kernel::System::ReferenceData')->TranslatedCountryList(
Language => $LayoutObject->{UserLanguage},
);
}
else {

# English name => English name
$CountryList = $Kernel::OM->Get('Kernel::System::ReferenceData')->CountryList;
}
$Param{Option} = $LayoutObject->BuildSelection(
Data => $CountryList,
PossibleNone => 1,
Expand All @@ -682,14 +690,11 @@ sub _Edit {
SelectedID => defined( $Param{ $Entry->[0] } ) ? $Param{ $Entry->[0] } : 1,
);
}
elsif ( $Entry->[0] =~ /^ValidID/i ) {
my $OptionRequired = '';
if ( $Entry->[4] ) {
$OptionRequired = 'Validate_Required';
}
elsif ( $Entry->[0] =~ m/^ValidID/i ) {

# build ValidID string
$Block = 'Option';
my $OptionRequired = $Entry->[4] ? 'Validate_Required' : '';
$Param{Option} = $LayoutObject->BuildSelection(
Data => { $ValidObject->ValidList(), },
Name => $Entry->[0],
Expand Down Expand Up @@ -755,6 +760,7 @@ sub _Edit {
}
}
}

return 1;
}

Expand Down
126 changes: 97 additions & 29 deletions Kernel/System/ReferenceData.pm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package Kernel::System::ReferenceData;
use v5.24;
use strict;
use warnings;
use namespace::autoclean;
use utf8;

# core modules

Expand All @@ -27,19 +29,44 @@ use Locale::Country qw(all_country_names);

# OTOBO modules

our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
our @ObjectDependencies = qw(
Kernel::Config
Kernel::System::Log
Kernel::System::Main
);

=for stopwords da CLDR
=head1 NAME
Kernel::System::ReferenceData - ReferenceData lib
=head1 DESCRIPTION
Contains reference data. For now, this is limited to just a list of ISO country
codes.
Contains reference data. For now, this is limited to:
=over 4
=item ISO 3166-1 country codes and English names
Currently there are 249 officially assigned codes.
Retired codes are not included.
=item Translated country names from the Unicode CLDR
The keys are two letter country codes. The codes are made up of:
=over 4
=item the 249 officially assigned ISO 3166-1 codes
=item exceptional reservations: Ascension Island, Clipperton Island, Diego Garcia, Ceuta and Melilla, Canary Islands, and Tristan da Cunha
=item user-assigned temporary country code: Kosovo
=back
=back
=head1 PUBLIC INTERFACE
Expand All @@ -55,44 +82,41 @@ sub new {
my ( $Type, %Param ) = @_;

# allocate new hash for object
my $Self = {};
bless( $Self, $Type );

return $Self;
return bless {}, $Type;
}

=head2 CountryList()
return a list of countries as a hash reference. The countries are based on ISO
3166-2 and are provided by the Perl module Locale::Code::Country, or optionally
3166-1 and are provided by the Perl module Locale::Code::Country, or optionally
from the SysConfig setting ReferenceData::OwnCountryList.
my $CountryList = $ReferenceDataObject->CountryList(
Result => 'CODE', # optional: returns CODE => Country pairs conform ISO 3166-2.
my $CountryName2Name = $ReferenceDataObject->CountryList()
or
my $CountryName2Code = $ReferenceDataObject->CountryList()
Result => 'CODE', # optional: returns CODE => Country pairs conform ISO 3166-1 alpha-2.
);
=cut

sub CountryList {
my ( $Self, %Param ) = @_;

if ( !defined $Param{Result} || $Param{Result} ne 'CODE' ) {
$Param{Result} = undef;
}

my $Countries = $Kernel::OM->Get('Kernel::Config')->Get('ReferenceData::OwnCountryList');
# Determine whether the values of the result should be the codes
my $ReturnCodes = ( $Param{Result} // '' ) eq 'CODE';

if ( $Param{Result} && $Countries ) {
my $OwnCountries = $Kernel::OM->Get('Kernel::Config')->Get('ReferenceData::OwnCountryList');
if ($OwnCountries) {

# return Code => Country pairs from SysConfig
return $Countries;
}
elsif ($Countries) {
return $OwnCountries if $ReturnCodes;

# return Country => Country pairs from SysConfig
my %CountryJustNames = map { $_ => $_ } values %$Countries;
my %CountryName2Name = map { $_ => $_ } values $OwnCountries->%*;

return \%CountryJustNames;
return \%CountryName2Name;
}

# get the country list from Locale::Country
Expand All @@ -105,21 +129,65 @@ sub CountryList {
);
}

if ( $Param{Result} ) {
if ($ReturnCodes) {

# return Code => Country pairs from ISO list
my %Countries;
my %CountryName2Code;
for my $Country (@CountryNames) {
$Countries{$Country} = country2code( $Country, 1 );
$CountryName2Code{$Country} = country2code( $Country, 1 );
}

return \%Countries;
return \%CountryName2Code;
}

# return Country => Country pairs from ISO list
my %CountryNames = map { $_ => $_ } @CountryNames;
my %CountryName2Name = map { $_ => $_ } @CountryNames;

return \%CountryName2Name;
}

=head2 TranslatedCountryList()
returns a mapping of translated country names to two letter country codes.
The translated country name are prepended by their flag.
The data is provided by L<Locale::CLDR>.
my $CountryName2Code = $ReferenceDataObject->TranslatedCountryList(
Language => 'de',
);
=cut

sub TranslatedCountryList {
my ( $Self, %Param ) = @_;

my $MainObject = $Kernel::OM->Get('Kernel::System::Main');

# Locale::CLDR is not required for OTOBO
return {} unless $MainObject->Require('Locale::CLDR');

# TODO: Check whether $Param{Language} is supported

# For getting the country flags.
# See https://en.wikipedia.org/wiki/Regional_indicator_symbol
# This indicators are in a sequence: ord(🇩) = ord('🇦') - ord('A') + ord('D')
my $Base = ord('🇦') - ord('A');
my %Letter2Indicator = map
{ $_ => chr( $Base + ord($_) ) }
( 'A' .. 'Z' );

my $Locale = Locale::CLDR->new( language_id => $Param{Language} );
my $AllRegions = $Locale->all_regions(); # includes regions like '001' => World
my %Code2Name;
for my $Code ( grep { length $_ == 2 } keys $AllRegions->%* ) {
my $Flag =
join '',
map { $Letter2Indicator{$_} }
split //, $Code;
$Code2Name{$Code} = "$AllRegions->{$Code} $Flag";
}

return \%CountryNames;
return \%Code2Name;
}

1;
19 changes: 19 additions & 0 deletions bin/otobo.CheckModules.pl
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,25 @@ =head1 DESCRIPTION
},
);

# Add CLDR language packs. It is not decided yet whether all 50 languages are added.
# So let's first go for the languages that have a translation quote of more than 80%.
# See https://translate.otobo.org/projects/otobo10/otobo/.
for my $Code (qw(De Nb Es Zh Pt Ar Hu Sr Ko Ru)) {
push @NeededModules,
{
Module => "Locale::CLDR::Locales::$Code",
Features => ['div:cldr'],
VersionRequired => '0.34.3',
Comment => 'localisation from the CLDR project',
InstTypes => {
aptget => undef, # not in any Debian package
emerge => undef,
zypper => undef,
ports => undef,
},
};
}

# Sanity check.
for my $Module (@NeededModules) {
die 'Module must be set!' unless defined $Module->{Module};
Expand Down
Loading

0 comments on commit f4d2dff

Please sign in to comment.