Compare commits

..
2 Commits
Author SHA256 Message Date
Daniel-Garmig 8232e7d7c9 GuestLog - Integrated AddressQuery dialog in GuestDocument creation and fixes.
- Added new CountryCodeUtils to map ISO-3166:Alpha2 country codes to ISO-3166:Alpha3 codes.
- Added new config for results language of AddressQuery services.
2026-08-11 22:02:15 +02:00
Daniel-Garmig 5c7fed2677 GuestLog - Added dialog for Address search. 2026-08-11 13:14:49 +02:00
9 changed files with 613 additions and 38 deletions
@@ -0,0 +1,160 @@
@using Somnia.GuestLog.Models.DTOs;
@using Somnia.GuestLog.Services.AddressQuery;
@inject ILogger<AddressQueryDialog> logger;
@inject IAddressQueryFactory addressQueryFactory;
@inject ISnackbar Snackbar
<MudDialog @ref="@DialogRef">
<TitleContent>
<MudText Typo="Typo.h6">
<MudIcon Icon="@Icons.Material.Filled.NotListedLocation" Class="mr-3 mb-n1" />
@LanguageLocale.control_AddressQueryDialog_dialogTitle
</MudText>
</TitleContent>
<DialogContent>
<MudStack row>
<MudTextField @bind-Value="SearchText" Label="@LanguageLocale.control_AddressQueryDialog_searchText"
Required />
<MudIconButton Icon="@Icons.Material.Filled.Search" OnClick="btnSearchAddress" />
</MudStack>
<MudPaper Class="mt-4 pa-2">
@if (addressResults.Count == 0)
{
<MudText Typo="Typo.body1">@LanguageLocale.general_noResults</MudText>
}
<MudList T="AddressResultDTO" @bind-SelectedValue="SelectedAddressResult"
SelectionMode="SelectionMode.SingleSelection" Color="Color.Info">
@foreach (AddressResultDTO result in addressResults)
{
<MudListItem Value="result">
<ChildContent>
<MudTextField Value="@($"{result.AddressRoad}, {result.HouseNumber}")"
Label="@LanguageLocale.general_address_address" ReadOnly />
<MudStack row>
<MudTextField Value="@result.AddressCity" Label="@LanguageLocale.general_address_city"
ReadOnly />
<MudTextField Value="@result.AddressProvince"
Label="@LanguageLocale.general_address_province" ReadOnly />
<MudTextField Value="@result.AddressState" Label="@LanguageLocale.general_address_state"
ReadOnly />
</MudStack>
<MudStack row>
<MudTextField Value="@result.AddressCountry" Label="@LanguageLocale.general_address_country"
ReadOnly />
<MudTextField Value="@result.ZipCode" Label="@LanguageLocale.general_address_zipCode"
ReadOnly />
</MudStack>
</ChildContent>
</MudListItem>
}
</MudList>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">
@LanguageLocale.general_btnCancel
</MudButton>
<MudButton Color="Color.Primary" OnClick="btnSelectAddress">
@LanguageLocale.control_AddressQueryDialog_btnSelectAddress
</MudButton>
</DialogActions>
</MudDialog>
@code {
// Dialog Instance injected when open by the DialogService.
// This field is null when dialog is not open by the DialogService.
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
// Reference to the Dialog when open by the ShowAsync Method. (When dialog is used inline).
// This field is null when dialog is not open using the internal methods.
private IDialogReference? _reference;
//Reference to the MudDialog component. Used to manage the internal dialog when the component is inline.
private MudDialog DialogRef { get; set; } = null!;
private string SearchText { get; set; } = "";
private List<AddressResultDTO> addressResults = [];
private AddressResultDTO? SelectedAddressResult { get; set; }
private void Cancel()
{
if (MudDialog is not null)
{
MudDialog.Cancel();
return;
}
else if (_reference is not null)
{
DialogRef.CloseAsync(DialogResult.Cancel());
return;
}
throw new InvalidOperationException("Unable to cancel (close) the dialog!");
}
private async Task btnSearchAddress()
{
if (SearchText.Length == 0)
{
return;
}
try
{
var queryService = addressQueryFactory.GetService();
addressResults = await queryService.SearchAddressAsync(SearchText);
}
catch (Exception ex)
{
logger.LogError(ex, "SearchAddress returned an error!");
Snackbar.Add($"Error searching address: {ex.Message}", Severity.Error);
}
}
private async Task btnSelectAddress()
{
if (SelectedAddressResult is null)
{
Snackbar.Add("Select an address", Severity.Warning);
return;
}
DialogResult dialogResult = DialogResult.Ok<AddressResultDTO?>(SelectedAddressResult);
//Close the dialog when opened by DialogService.
if (MudDialog != null)
{
logger.LogDebug("AddressQueryDialog - Closed MudDialogInstance!");
MudDialog.Close(dialogResult);
return;
}
//Close the dialog when is a Inline dialog.
else if (_reference is not null)
{
logger.LogDebug("AddressQueryDialog - Closed inline dialog instance!");
await DialogRef.CloseAsync(dialogResult);
return;
}
//Throw if the dialog can't be closed (as none of the above is true).
throw new InvalidOperationException("Dialog cannot be closed! No reference to close!");
}
public async Task<IDialogReference> ShowAsync(DialogOptions? options = null)
{
_reference = await DialogRef.ShowAsync(options: options);
return _reference;
}
public async Task CloseAsync()
{
await DialogRef.CloseAsync();
_reference = null;
}
}
@@ -17,14 +17,16 @@
<MudDivider DividerType="DividerType.Middle" Class="mb-4" />
<MudStack Row style="@FieldMarginY">
<MudSelect @bind-Value="Model.DocumentType" For="@(() => Model.DocumentType)" Label="@LanguageLocale.dto_GuestDocument_DocumentType"
Required ReadOnly="true">
Required ReadOnly="@ReadOnlyDocId">
@foreach (string docType in dataListService.GetDataListByName(GuestConsts.DataList_DocumentTypes))
{
<MudSelectItem Value="docType">@dataListService.GetLocalizedValue(GuestConsts.DataList_DocumentTypes, docType)</MudSelectItem>
<MudSelectItem Value="docType">
@dataListService.GetLocalizedValue(GuestConsts.DataList_DocumentTypes, docType)
</MudSelectItem>
}
</MudSelect>
<MudTextField @bind-Value="Model.DocumentNum" For="@(() => Model.DocumentNum)" Label="@LanguageLocale.dto_GuestDocument_DocumentNum"
Immediate="true" Required ReadOnly="true" />
Immediate="true" Required ReadOnly="@ReadOnlyDocId" />
</MudStack>
<MudStack Row style="@FieldMarginY">
@@ -64,7 +66,9 @@
<MudSelect @bind-Value="Model.Gender" For="@(() => Model.Gender)" Label="@LanguageLocale.dto_GuestDocument_Gender">
@foreach (string gender in dataListService.GetDataListByName(GuestConsts.DataList_GenderOptions))
{
<MudSelectItem Value="gender">@dataListService.GetLocalizedValue(GuestConsts.DataList_GenderOptions, gender)</MudSelectItem>
<MudSelectItem Value="gender">
@dataListService.GetLocalizedValue(GuestConsts.DataList_GenderOptions, gender)
</MudSelectItem>
}
</MudSelect>
</MudStack>
@@ -92,23 +96,31 @@
<MudText Typo="Typo.subtitle1" Align="Align.Center">@LanguageLocale.control_GuestDocForm_sectionAddress</MudText>
<MudDivider DividerType="DividerType.Middle" Class="mb-4" />
<MudTextField @bind-Value="Model.Address" For="@(() => Model.Address)" Immediate="true" Label="@LanguageLocale.dto_GuestDocument_Address" />
@if (!ReadOnly)
{
<MudButton OnClick="BtnOpenAddressSearchDialog" Variant="Variant.Outlined" FullWidth="true" Class="mb-2">
@LanguageLocale.control_GuestDocForm_btnSearchAddress
</MudButton>
}
<MudTextField @bind-Value="Model.Address" For="@(() => Model.Address)"
Label="@LanguageLocale.general_address_address" ReadOnly="@ReadOnlyAddress" />
<MudStack Row style="@FieldMarginY">
<MudTextField @bind-Value="Model.AddressCity" For="@(() => Model.AddressCity)"
Label="@LanguageLocale.dto_GuestDocument_AddressCity" Immediate="true" />
Label="@LanguageLocale.general_address_city" ReadOnly="@ReadOnlyAddress" />
<MudTextField @bind-Value="Model.AddressProvince" For="@(() => Model.AddressProvince)"
Label="@LanguageLocale.dto_GuestDocument_AddressProvince" Immediate="true" />
Label="@LanguageLocale.general_address_province" ReadOnly="@ReadOnlyAddress" />
<MudTextField @bind-Value="Model.AddressState" For="@(() => Model.AddressState)"
Label="@LanguageLocale.dto_GuestDocument_AddressState" Immediate="true" />
Label="@LanguageLocale.general_address_state" ReadOnly="@ReadOnlyAddress" />
</MudStack>
<MudStack Row style="@FieldMarginY">
<MudAutocomplete T="string" @bind-Value="Model.AddressCountry" For="@(() => Model.AddressCountry)"
Label="@LanguageLocale.dto_GuestDocument_AddressCountry"
Label="@LanguageLocale.general_address_country" ReadOnly="@ReadOnlyAddress"
SearchFunc="@SearchCountryCodes" ToStringFunc="@(e=> string.IsNullOrEmpty(e) ? "" : @dataListService.GetLocalizedValue(GuestConsts.DataList_CountryCodes, e))"
SelectValueOnTab="true" AdornmentIcon="@Icons.Material.Filled.Search" />
<MudTextField @bind-Value="Model.ZipCode" For="@(() => Model.ZipCode)" Immediate="true"
Label="@LanguageLocale.dto_GuestDocument_ZipCode" />
<MudTextField @bind-Value="Model.ZipCode" For="@(() => Model.ZipCode)"
Label="@LanguageLocale.general_address_zipCode" ReadOnly="@ReadOnlyAddress" />
</MudStack>
</MudPaper>
</MudItem>
@@ -116,6 +128,8 @@
<MudText Class="mt-4">@CreateTimeString</MudText>
</MudForm>
<AddressQueryDialog @ref="@AddressQueryDialog" />
@code {
[Parameter]
@@ -124,9 +138,21 @@
[Parameter]
public bool ReadOnly { get; set; } = false;
[Parameter]
public bool ReadOnlyDocId { get; set; } = true;
private MudForm _form = null!;
AddressQueryDialog AddressQueryDialog { get; set; } = null!;
DialogOptions AddressQueryDialogOptions = new()
{
MaxWidth = MaxWidth.Large,
FullWidth = true,
};
private bool ReadOnlyAddress { get; set; } = true;
private const string FieldMarginY = "margin-block: 0.5em";
private Dictionary<string, string>? countryDict = null;
private bool isSuportNumberRequired { get => Model?.Nationality == GuestConsts.CountryCode_ESP; }
@@ -178,4 +204,39 @@
.Select(x => x.Key);
}
#region Event Handlers
private async Task BtnOpenAddressSearchDialog()
{
var reference = await AddressQueryDialog.ShowAsync(options: AddressQueryDialogOptions);
DialogResult? result = await reference.Result;
if (result is null || result.Canceled)
{
return;
}
AddressResultDTO? resultData = await reference.GetReturnValueAsync<AddressResultDTO>();
if (resultData is null)
{
return;
}
//Update Address info.
Model.Address = $"{resultData.AddressRoad}, {resultData.HouseNumber}";
Model.AddressCity = resultData.AddressCity;
Model.AddressProvince = resultData.AddressProvince;
Model.AddressState = resultData.AddressState;
Model.ZipCode = resultData.ZipCode;
//Set country by address code.
Model.AddressCountry = resultData.AddressCountryCode;
//Address is editable after searching (if the form is editable).
ReadOnlyAddress = ReadOnly;
}
#endregion
}
@@ -6,16 +6,20 @@
@inject ILogger<AddressQuery> logger;
@inject IAddressQueryFactory addressQueryFactory;
@inject ISnackbar Snackbar
@inject IDialogService DialogService
<PageTitle>DEBUG - Address Query</PageTitle>
<MudText Typo="Typo.h3" GutterBottom="true">Address Search</MudText>
<MudText Typo="Typo.h3" GutterBottom="true" Class="mt-2">Address Search</MudText>
<MudPaper Class="ma-2 pa-4">
<MudStack Row>
<MudTextField @ref="txtSearch" @bind-Value="SearchText" Label="Search address" Required />
<MudButton Color="Color.Primary" Variant="Variant.Filled" @onclick="btnSearchAddress" Class="mt-2">Search
</MudButton>
</MudStack>
<MudButton Color="Color.Primary" Variant="Variant.Filled" @onclick="btnSearchAddress">Search</MudButton>
<MudPaper>
<MudPaper class="mt-2 pa-4">
@if (addressResults.Count == 0)
{
<MudText Typo="Typo.body1">No results!</MudText>
@@ -34,6 +38,30 @@
</MudItem>
}
</MudPaper>
</MudPaper>
<MudDivider DividerType="DividerType.FullWidth" />
<MudPaper Class="ma-2 pa-4">
<MudButton OnClick="btnOpenSearchDialog" Color="Color.Primary" Variant="Variant.Filled" FullWidth>
Open Search Dialog
</MudButton>
@if (dialogSelectedAddress is not null)
{
<MudPaper class="mt-2 pa-4">
<MudText>Selected Address: </MudText>
<MudStack>
<MudText>Address: @dialogSelectedAddress.AddressRoad , @dialogSelectedAddress.HouseNumber</MudText>
<MudText>City: @dialogSelectedAddress.AddressCity</MudText>
<MudText>Province: @dialogSelectedAddress.AddressProvince</MudText>
<MudText>State: @dialogSelectedAddress.AddressState</MudText>
<MudText>Country: @dialogSelectedAddress.AddressCountry</MudText>
<MudText>ZipCode: @dialogSelectedAddress.ZipCode</MudText>
</MudStack>
</MudPaper>
}
</MudPaper>
@code {
@@ -42,6 +70,8 @@
private MudTextField<string> txtSearch = null!;
private AddressResultDTO? dialogSelectedAddress = null;
private async Task btnSearchAddress()
{
if (SearchText.Length == 0)
@@ -62,4 +92,23 @@
Snackbar.Add($"Error searching address: {ex.Message}", Severity.Error);
}
}
private async Task btnOpenSearchDialog()
{
DialogOptions options = new()
{
MaxWidth = MaxWidth.Large,
FullWidth= true,
};
var dialog = await DialogService.ShowAsync<AddressQueryDialog>("Search Address", options);
DialogResult? result = await dialog.Result;
if (result is not null && !result.Canceled)
{
AddressResultDTO? resultData = await dialog.GetReturnValueAsync<AddressResultDTO>();
dialogSelectedAddress = resultData;
}
}
}
@@ -9,6 +9,7 @@ public class AddressQueryConfig
public string? ServiceURL { get; set; }
public string? ApiKey { get; set; }
public string? UserAgent { get; set; }
public string? ResultLang { get; set; }
public enum AddressQueryProviderEnum
@@ -9,4 +9,9 @@ public class AddressResultDTO
public string? AddressState { get; set; }
public string? AddressCountry { get; set; }
public string? ZipCode { get; set; }
/// <summary>
/// CountryCode in ISO3166-1:alpha3
/// </summary>
public string? AddressCountryCode { get; set; }
}
@@ -134,12 +134,6 @@
<data name="dto.GuestDocument.Gender" xml:space="preserve"><value>Gender</value></data>
<data name="dto.GuestDocument.PhoneNumber" xml:space="preserve"><value>Phone Number</value></data>
<data name="dto.GuestDocument.EmailAddress" xml:space="preserve"><value>Email Address</value></data>
<data name="dto.GuestDocument.Address" xml:space="preserve"><value>Address</value></data>
<data name="dto.GuestDocument.AddressCity" xml:space="preserve"><value>City</value></data>
<data name="dto.GuestDocument.AddressProvince" xml:space="preserve"><value>County</value></data>
<data name="dto.GuestDocument.AddressState" xml:space="preserve"><value>State</value></data>
<data name="dto.GuestDocument.AddressCountry" xml:space="preserve"><value>Country</value></data>
<data name="dto.GuestDocument.ZipCode" xml:space="preserve"><value>Zip Code</value></data>
<data name="dto.GuestDocument.CreateTime" xml:space="preserve"><value>Creation Time</value></data>
<data name="dto.GuestDocument.UpdateTime" xml:space="preserve"><value>Update Time</value></data>
@@ -156,8 +150,16 @@
<data name="general.btnSearch" xml:space="preserve"><value>Search</value></data>
<data name="general.btnSave" xml:space="preserve"><value>Save</value></data>
<data name="general.btnCancel" xml:space="preserve"><value>Cancel</value></data>
<data name="general.noResults" xml:space="preserve"><value>No Results!</value></data>
<data name="general.wildcardHelperText" xml:space="preserve"><value>Use '%' as wildcard</value></data>
<data name="general.address.address" xml:space="preserve"><value>Address</value></data>
<data name="general.address.city" xml:space="preserve"><value>City</value></data>
<data name="general.address.province" xml:space="preserve"><value>County</value></data>
<data name="general.address.state" xml:space="preserve"><value>State</value></data>
<data name="general.address.country" xml:space="preserve"><value>Country</value></data>
<data name="general.address.zipCode" xml:space="preserve"><value>Zip Code</value></data>
<data name="page.GuestDocList.pageTitle" xml:space="preserve"><value>Guest Documents</value></data>
<data name="page.GuestDocList.documentList" xml:space="preserve"><value>Document List</value></data>
<data name="page.GuestDocList.btnNewGuest" xml:space="preserve"><value>New Guest</value></data>
@@ -170,5 +172,11 @@
<data name="control.GuestDocForm.sectionGuest" xml:space="preserve"><value>Guest Details</value></data>
<data name="control.GuestDocForm.sectionContact" xml:space="preserve"><value>Contact Details</value></data>
<data name="control.GuestDocForm.sectionAddress" xml:space="preserve"><value>Guest Address</value></data>
<data name="control.GuestDocForm.btnSearchAddress" xml:space="preserve"><value>Search Address</value></data>
<data name="control.AddressQueryDialog.dialogTitle" xml:space="preserve"><value>Search Address</value></data>
<data name="control.AddressQueryDialog.searchText" xml:space="preserve"><value>Search Address</value></data>
<data name="control.AddressQueryDialog.btnSearch" xml:space="preserve"><value>Search Address</value></data>
<data name="control.AddressQueryDialog.btnSelectAddress" xml:space="preserve"><value>Use Address</value></data>
</root>
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Options;
using Somnia.GuestLog.Models.Configuration;
using Somnia.GuestLog.Models.DTOs;
using Somnia.GuestLog.Utils;
namespace Somnia.GuestLog.Services.AddressQuery;
@@ -36,6 +37,8 @@ public class NominatimAddressQueryService(
continue;
}
string? alpha3CountryCode = CountryCodesUtils.Alpha2ToAlpha3(result.Address.Country_code);
rsList.Add(new()
{
HouseNumber = result.Address.House_number,
@@ -44,7 +47,8 @@ public class NominatimAddressQueryService(
AddressProvince = result.Address.Province,
AddressState = result.Address.State,
AddressCountry = result.Address.Country,
ZipCode = result.Address.Postcode
ZipCode = result.Address.Postcode,
AddressCountryCode = alpha3CountryCode
});
}
@@ -66,7 +70,10 @@ public class NominatimAddressQueryService(
client.BaseAddress = baseAddr;
client.DefaultRequestHeaders.UserAgent.ParseAdd(config.UserAgent ?? defaultUserAgent);
client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("es");
if (config.ResultLang is not null)
{
client.DefaultRequestHeaders.AcceptLanguage.ParseAdd(config.ResultLang);
}
return client;
}
@@ -92,7 +99,8 @@ public class NominatimAddressQueryService(
string? Province = null,
string? State = null,
string? Country = null,
string? Postcode = null
string? Postcode = null,
string? Country_code = null
);
private record class ApiResult(
@@ -0,0 +1,282 @@
namespace Somnia.GuestLog.Utils;
public static class CountryCodesUtils
{
// Returns null when input is invalid/unknown.
public static string? Alpha2ToAlpha3(string? alpha2)
{
if (alpha2 is null) return null;
// Common normalization: trim + uppercase.
// Avoids issues with "us", " US ", etc.
var key = alpha2.Trim().ToUpperInvariant();
if (key.Length != 2) return null;
return Alpha2ToAlpha3Dict.TryGetValue(key, out var alpha3) ? alpha3 : null;
}
// Throwing for invalid/unknown codes
public static string Alpha2ToAlpha3OrThrow(string alpha2)
{
var result = Alpha2ToAlpha3(alpha2)
?? throw new ArgumentException("Unknown or invalid ISO 3166-1 alpha2 code.", nameof(alpha2));
return result;
}
// Build-time constant lookup.
// Keys are ISO 3166-1 alpha-2 codes (uppercase).
private static readonly Dictionary<string, string> Alpha2ToAlpha3Dict =
new(StringComparer.Ordinal)
{
["AF"] = "AFG",
["AX"] = "ALA",
["AL"] = "ALB",
["DZ"] = "DZA",
["AS"] = "ASM",
["AD"] = "AND",
["AO"] = "AGO",
["AI"] = "AIA",
["AQ"] = "ATA",
["AG"] = "ATG",
["AR"] = "ARG",
["AM"] = "ARM",
["AW"] = "ABW",
["AU"] = "AUS",
["AT"] = "AUT",
["AZ"] = "AZE",
["BS"] = "BHS",
["BH"] = "BHR",
["BD"] = "BGD",
["BB"] = "BRB",
["BY"] = "BLR",
["BE"] = "BEL",
["BZ"] = "BLZ",
["BJ"] = "BEN",
["BM"] = "BMU",
["BT"] = "BTN",
["BO"] = "BOL",
["BQ"] = "BES",
["BA"] = "BIH",
["BW"] = "BWA",
["BV"] = "BVT",
["BR"] = "BRA",
["IO"] = "IOT",
["BN"] = "BRN",
["BG"] = "BGR",
["BF"] = "BFA",
["BI"] = "BDI",
["CV"] = "CPV",
["KH"] = "KHM",
["CM"] = "CMR",
["CA"] = "CAN",
["KY"] = "CYM",
["CF"] = "CAF",
["TD"] = "TCD",
["CL"] = "CHL",
["CN"] = "CHN",
["CX"] = "CXR",
["CC"] = "CCK",
["CO"] = "COL",
["KM"] = "COM",
["CG"] = "COG",
["CD"] = "COD",
["CK"] = "COK",
["CR"] = "CRI",
["CI"] = "CIV",
["HR"] = "HRV",
["CU"] = "CUB",
["CW"] = "CUW",
["CY"] = "CYP",
["CZ"] = "CZE",
["DK"] = "DNK",
["DJ"] = "DJI",
["DM"] = "DMA",
["DO"] = "DOM",
["EC"] = "ECU",
["EG"] = "EGY",
["SV"] = "SLV",
["GQ"] = "GNQ",
["ER"] = "ERI",
["EE"] = "EST",
["SZ"] = "SWZ",
["ET"] = "ETH",
["FK"] = "FLK",
["FO"] = "FRO",
["FJ"] = "FJI",
["FI"] = "FIN",
["FR"] = "FRA",
["GF"] = "GUF",
["PF"] = "PYF",
["TF"] = "ATF",
["GA"] = "GAB",
["GM"] = "GMB",
["GE"] = "GEO",
["DE"] = "DEU",
["GH"] = "GHA",
["GI"] = "GIB",
["GR"] = "GRC",
["GL"] = "GRL",
["GD"] = "GRD",
["GP"] = "GLP",
["GU"] = "GUM",
["GT"] = "GTM",
["GG"] = "GGY",
["GN"] = "GIN",
["GW"] = "GNB",
["GY"] = "GUY",
["HT"] = "HTI",
["HM"] = "HMD",
["VA"] = "VAT",
["HN"] = "HND",
["HK"] = "HKG",
["HU"] = "HUN",
["IS"] = "ISL",
["IN"] = "IND",
["ID"] = "IDN",
["IR"] = "IRN",
["IQ"] = "IRQ",
["IE"] = "IRL",
["IM"] = "IMN",
["IL"] = "ISR",
["IT"] = "ITA",
["JM"] = "JAM",
["JP"] = "JPN",
["JE"] = "JEY",
["JO"] = "JOR",
["KZ"] = "KAZ",
["KE"] = "KEN",
["KI"] = "KIR",
["KP"] = "PRK",
["KR"] = "KOR",
["KW"] = "KWT",
["KG"] = "KGZ",
["LA"] = "LAO",
["LV"] = "LVA",
["LB"] = "LBN",
["LS"] = "LSO",
["LR"] = "LBR",
["LY"] = "LBY",
["LI"] = "LIE",
["LT"] = "LTU",
["LU"] = "LUX",
["MO"] = "MAC",
["MG"] = "MDG",
["MW"] = "MWI",
["MY"] = "MYS",
["MV"] = "MDV",
["ML"] = "MLI",
["MT"] = "MLT",
["MH"] = "MHL",
["MQ"] = "MTQ",
["MR"] = "MRT",
["MU"] = "MUS",
["YT"] = "MYT",
["MX"] = "MEX",
["FM"] = "FSM",
["MD"] = "MDA",
["MC"] = "MCO",
["MN"] = "MNG",
["ME"] = "MNE",
["MS"] = "MSR",
["MA"] = "MAR",
["MZ"] = "MOZ",
["MM"] = "MMR",
["NA"] = "NAM",
["NR"] = "NRU",
["NP"] = "NPL",
["NL"] = "NLD",
["NC"] = "NCL",
["NZ"] = "NZL",
["NI"] = "NIC",
["NE"] = "NER",
["NG"] = "NGA",
["NU"] = "NIU",
["NF"] = "NFK",
["MK"] = "MKD",
["MP"] = "MNP",
["NO"] = "NOR",
["OM"] = "OMN",
["PK"] = "PAK",
["PW"] = "PLW",
["PS"] = "PSE",
["PA"] = "PAN",
["PG"] = "PNG",
["PY"] = "PRY",
["PE"] = "PER",
["PH"] = "PHL",
["PN"] = "PCN",
["PL"] = "POL",
["PT"] = "PRT",
["PR"] = "PRI",
["QA"] = "QAT",
["RE"] = "REU",
["RO"] = "ROU",
["RU"] = "RUS",
["RW"] = "RWA",
["BL"] = "BLM",
["SH"] = "SHN",
["KN"] = "KNA",
["LC"] = "LCA",
["MF"] = "MAF",
["PM"] = "SPM",
["VC"] = "VCT",
["WS"] = "WSM",
["SM"] = "SMR",
["ST"] = "STP",
["SA"] = "SAU",
["SN"] = "SEN",
["RS"] = "SRB",
["SC"] = "SYC",
["SL"] = "SLE",
["SG"] = "SGP",
["SX"] = "SXM",
["SK"] = "SVK",
["SI"] = "SVN",
["SB"] = "SLB",
["SO"] = "SOM",
["ZA"] = "ZAF",
["GS"] = "SGS",
["SS"] = "SSD",
["ES"] = "ESP",
["LK"] = "LKA",
["SD"] = "SDN",
["SR"] = "SUR",
["SJ"] = "SJM",
["SE"] = "SWE",
["CH"] = "CHE",
["SY"] = "SYR",
["TW"] = "TWN",
["TJ"] = "TJK",
["TZ"] = "TZA",
["TH"] = "THA",
["TL"] = "TLS",
["TG"] = "TGO",
["TK"] = "TKL",
["TO"] = "TON",
["TT"] = "TTO",
["TN"] = "TUN",
["TR"] = "TUR",
["TM"] = "TKM",
["TC"] = "TCA",
["TV"] = "TUV",
["UG"] = "UGA",
["UA"] = "UKR",
["AE"] = "ARE",
["GB"] = "GBR",
["US"] = "USA",
["UM"] = "UMI",
["UY"] = "URY",
["UZ"] = "UZB",
["VU"] = "VUT",
["VE"] = "VEN",
["VN"] = "VNM",
["VG"] = "VGB",
["VI"] = "VIR",
["WF"] = "WLF",
["EH"] = "ESH",
["YE"] = "YEM",
["ZM"] = "ZMB",
["ZW"] = "ZWE",
};
}
+2 -1
View File
@@ -14,6 +14,7 @@
"Provider": "Nominatim",
"ServiceURL": "https://nominatim.openstreetmap.org/",
"ApiKey": "",
"UserAgent": "Somnia-GuestLog-Dev"
"UserAgent": "Somnia-GuestLog-Dev",
"ResultLang": "es"
}
}