mirror of
https://github.com/we-promise/sure.git
synced 2026-05-25 13:34:58 +00:00
* feat(i18n): complete Catalan translations + extract residual hardcoded strings
CA coverage
- All view/model/breadcrumb/doorkeeper/mailer locale files for ca: 0 missing
keys (was ~3,400). Translations follow informal "tu" register, sentence case,
domain glossary (Compte/Saldo/Transacció/Posició/Operació/Pressupost/...).
- Catalan pluralization test: ca uses one/other; mirrors
test/lib/polish_pluralization_test.rb.
- 8 LanguageTool-flagged grammar fixes applied (Connexió òrfena, Secret de
l'API, comma-pero, apostrophe elisions, etc).
Hardcoded string extraction (also fixes EN parity)
- UI::Account::Chart#title + chart.html.erb view tabs -> UI.account.chart.*
- UI::Account::BalanceReconciliation labels + tooltips ->
UI.account.balance_reconciliation.{labels,tooltips}.*
- transactions/_transfer_match.html.erb (Auto-matched, A/M, Confirm/Reject
match, Payment/Transfer is confirmed) -> transactions.transfer_match.*
- AccountOrder labels (Name/Balance asc/desc) -> account_order.* keys with
fallback to existing hardcoded labels.
- Depository::SUBTYPES surface in account list -> depositories.subtypes.*.*
- User role badge -> users.roles.* (admin / member / super_admin).
- 110+ country names -> countries.* (config/locales/countries.ca.yml).
Breadcrumb locale fix
- Breadcrumbable was a before_action that ran before Localize's around_action
switched I18n.locale, so default crumbs rendered in EN even when locale=ca.
- Convert to helper_method that defers translation to render-time (when
I18n.locale is already correct). Add all missing breadcrumb keys to ca + en.
- Layouts switched from @breadcrumbs to breadcrumbs helper.
Locale-aware helpers / formatters
- ApplicationHelper#localized_ordinal: ordinalize that respects ca
(1r/2n/3r/4t/Nè). Wired into preferences month_start_day select.
- Family#moniker_label / moniker_label_plural: translate the default "Family"/
"Group" monikers via shared.family_moniker.* with fallback to the family's
custom override.
- Budget#name: use I18n.l for month_year/short/long instead of strftime("%B %Y")
so the budget header date follows the active locale.
Tooling
- script/lt_check_ca.rb: batched LanguageTool checker (premium endpoint when
LT_USERNAME/LT_API_KEY are set, free fallback otherwise), picky mode,
motherTongue=en for false-friend detection.
- lib/tasks/i18n_screenshot.rake: dev-only rake to set user.locale=ca and
role=super_admin on the demo user so the i18n surfaces can be walked.
Out of scope (pre-existing, not introduced here)
- Native browser file input "Choose Files / No file chosen" (browser locale).
- D3.js client-side chart x-axis dates (JS-side Intl.DateTimeFormat needed).
- Sankey/donut labels = seed category names (data, not i18n).
- 2 rails-i18n datetime/errors interpolation warnings inherited from
config/locales/defaults/ca.yml.
* fix(i18n): apply idiomatic Catalan review (3-agent + native review)
Three parallel review agents flagged 203 findings (31 high / 73 medium / 99 low)
across all 111 ca.yml files. This commit applies the high-severity bugs plus a
curated subset of medium-impact fixes.
Grammar / agreement
- provider_sync_summary.health.stale_pending: `(exclòs)` -> `(exclosa/excloses)`
to agree with feminine `transacció(s)`.
- accounts.confirm_unlink.warning_no_sync: added reflexive `es` -
`el compte ja no es sincronitzarà`.
- sophtron_setup_required.heading: `no configurats` -> `sense configurar`
(avoids broken agreement across "ID" masc. + "clau" fem.).
- admin.sso_providers.form.errors_title: split into one/other pluralization
keys (en + ca); singular `ha impedit` was wrong for count > 1.
Brand consistency
- IndexaCapital -> Indexa Capital (37 occurrences across one file).
- Lunchflow -> Lunch Flow in two remaining places.
Anglicisms / domain mistranslations
- kraken_items setup_accounts.instructions: `ompliments d'operacions`
(lit. dental/food fillings) -> `execucions d'operacions`.
- settings kraken_panel.read_only_title: `Sincronització d'intercanvi`
(swap/trade) -> `Sincronització només de lectura amb l'exchange`.
- transactions convert_to_trade.security_custom + security_not_listed_hint:
`cotització` (price quote) -> `ticker` (the EN field IS a ticker symbol).
- loans.form.rate_type: `Tipus d'interès` collided with sibling
interest_rate -> `Modalitat del tipus`.
- brex_items.provider_panel.sandbox_note_html: `L'staging` (broken
contraction) -> `el staging`.
Idiom traps
- coinbase/binance/kraken wait_for_sync: `acabi de sincronitzar` is
ambiguous in CA (`acabar de + inf` reads as "has just done X") ->
`acabi la sincronització`.
- chats.ai_greeting.there: `a tothom` -> `''` (the EN fallback "Hey there"
is singular; literal CA `tothom` is plural and wrong for 1:1 chat).
- transactions.split_parent_row.split_label: `Divideix` (imperative) is
wrong as a status badge -> `Divisió` (noun).
- transactions.keep_both (2 occurrences): infinitive `mantenir ambdues` ->
imperative `mantén-les totes dues` to match the sibling Yes/No buttons.
- rules.clear_ai_cache: `Reinicia` (restart) -> `Buida` (empty/clear),
which matches the success notice (`s'està netejant`).
Moniker gender breakage (cross-file)
%{moniker} is interpolated downcased from family.moniker_label and may
resolve to feminine `família`/`llar` or masculine `grup`. Strings that
hard-code a gendered article ('al teu %{moniker}', 'aquesta %{moniker}',
'aquest/a %{moniker}') broke on at least one branch. Restructured the
affected sentences to drop the gendered determiner:
- account_sharings.show.no_members
- merchants.family_empty / family_title / provider_empty
- registrations.new.join_family_title
- settings.preferences.show.currencies_subtitle / sharing_subtitle
- simplefin_items.select_existing_account.no_accounts_found
- invitations.new.subtitle
- invitation_mailer.invite_email.subject (mailers/) + body (views/)
- snaptrade_items.providers.snaptrade.free_tier_warning
Terminology consistency
- models/account_statement/ca.yml attributes aligned with view-side
forms: `Saldo d'obertura`/`Saldo de tancament` ->
`Saldo inicial`/`Saldo final`; `Suggeriment de...` -> `Pista de...`.
- account_statements.coverage.status.not_expected:
`No s'esperava` -> `No previst` (status label, not past action).
- account_statements.index.empty_unmatched: aligned with the section's
own label `Safata sense aparellar`.
- imports.create.document_provider_not_configured + document_upload_failed:
`arxiu vectorial` -> `magatzem vectorial` (correct TermCat term).
- coinstats_items blockchain gender: `els blockchains` / `un blockchain` ->
`les blockchains` / `una blockchain` (feminine per TermCat).
- accounts.account.remove_default: `Treu el predeterminat` ->
`Treu com a predeterminat` (pairs with sibling `Estableix com a
predeterminat`).
- accounts.tax_treatments.tax_deferred: `Diferit fiscalment` (lit. calque)
-> `Tributació diferida` (standard CA tax-accounting term).
- settings.payments.show.currently_on_plan: `Actualment al` ->
`Actualment al pla:` (was a fragment).
Out of scope (review flagged, not applied here)
- LOW-severity stylistic preferences (Veure vs Mostra, etc).
- `models/category/ca.yml` default category names — seeded at family
creation, not via I18n at runtime, so changes wouldn't affect existing
families.
- `models/period/ca.yml` short labels mixing EN (MTD/YTD) and CA (STD/MA)
— needs a one-convention decision separately.
* fix(i18n,ca): drop gendered article in period_activity + tighten cash-flow terms
- pages.dashboard.investment_summary.period_activity: 'Activitat del
%{period}' contracted 'del' = 'de el' (masc.sg.). %{period} resolves
to mixed forms ('Setmana en curs' fem, 'Últims 30 dies' pl., 'Any en
curs' apostrophe), so hard-coded 'del' was wrong on most labels.
Replaced with 'Activitat — %{period}' (em-dash) to skip the
contraction entirely.
- pages.dashboard.outflows_donut.title / total_outflows: switched from
bare 'Sortides' / 'Total de sortides' to 'Sortides de caixa' /
'Total de sortides de caixa' to match TermCat's precise term
('sortida de caixa' = cash outflow).
* fix(i18n,ca): rephrase transfer source/destination amount labels
'Import d'origen' / 'Import de destinació' were literal calques of
'Source amount' / 'Destination amount'. In a multi-currency transfer
form (sender/receiver in different currencies) the natural CA pair is
'Import enviat' / 'Import rebut'.
* fix(i18n,ca): 'Dades en brut' -> 'Dades sense processar'
The literal calque of 'Raw data' read as too technical for personal-
finance UI. 'Dades sense processar' is the more natural Catalan
equivalent for raw/unprocessed data files.
* fix(i18n): localize Import col_sep label + separator options
The CSV upload form rendered 'Col sep' (the auto-humanized attribute
name) plus hardcoded English 'Comma (,)' / 'Semicolon (;)' options
from Import::SEPARATORS.
- activerecord.attributes.import.col_sep added (en + ca: 'Column
separator' / 'Separador de columnes').
- Import.separator_options class method returns translated tuples;
view switched from Import::SEPARATORS to Import.separator_options.
- activerecord.attributes.import.col_seps.{comma,semicolon} added so
the option labels follow the active locale.
* fix(i18n,ca): drop moniker apposition in sharing/currencies section titles
- sharing_title 'Compartició de %{moniker}' rendered as 'Compartició
de Família' (a noun-noun apposition that's odd in CA) -> 'Compartició
de comptes'.
- sharing_subtitle replaced '%{moniker}' with 'entre els membres' so
the sentence reads naturally and doesn't depend on moniker gender.
- currencies_title 'Divises de %{moniker}' had the same apposition
-> 'Divises'. Subtitle no longer references moniker either.
* fix(i18n,ca): keep 'Self Hosting' untranslated
Reverted 'Autoallotjament' / 'autoallotjada' / 'autoallotjats' usages
to the original English 'Self Hosting' (sidebar label, breadcrumbs,
hostings page title, chat assistant settings hint, redis configuration
subheading, LLM usages cost-estimates description).
The brand-style term reads more naturally in EN for technical users
configuring their own deployment.
* fix(i18n,ca): lowercase 'self hosting' (sentence case in labels)
* fix(i18n): extract budget_categories stepper + allocation_progress strings
Hardcoded English strings on the budget category editor:
- 'Setup' / 'Categories' stepper labels in budgets/_budget_nav.html.erb
- 'X% set' / '> 100% set' / 'left to allocate' / 'Budget exceeded by ...'
in budget_categories/_allocation_progress.erb
- '/m avg' caption + 'Shared' placeholder + 'Leave empty to share
parent's budget' tooltip in budget_categories/_budget_category_form
and _uncategorized_budget_category_form
Extracted to:
- budgets.budget_nav.{setup,categories}
- budget_categories.allocation_progress.{percent_set,over_set,left_to_allocate,budget_exceeded_html}
- budget_categories.budget_category_form.{monthly_average,shared_placeholder,shared_title}
CA translations added; EN keys mirror the prior literals.
* chore(i18n): drop translation tooling from PR
These were dev-only helpers used during the Catalan translation pass:
- script/lt_check_ca.rb: LanguageTool API checker (premium/free
endpoint, picky mode, batching). Useful for ongoing locale QA but
shouldn't ship in this feature PR.
- lib/tasks/i18n_screenshot.rake: rake task that flips user.locale and
role on the demo user for walking the i18n surfaces locally.
Both stay available locally; pulled out of the PR scope.
* fix(i18n): apply PR review feedback (CodeRabbit + Codex)
- balance_reconciliation crypto_items: use :end_balance_crypto tooltip
(was :end_balance_investment). Added new UI.account.balance_reconciliation.tooltips.end_balance_crypto key in en + ca.
- doorkeeper.ca.yml confidentiality.no: was YAML boolean false, now string 'No'.
- views/categories: 'Poor contrast, choose darker color or' continued with hardcoded 'auto-adjust.' button text; extracted to categories.form.auto_adjust key (en + ca).
- imports.create.document_upload_failed: 'a l'magatzem' was broken
contraction -> 'al magatzem'.
- invitation_mailer body + mailer subject: 'unir-se' -> 'unir-te' (was
3rd person, should be 2nd to match the rest of the copy).
- 7 strings across mercury_items / sophtron_items / simplefin_items /
lunchflow_items / brex_items / indexa_capital_items / other_assets:
'se sincronitzaran' -> 'es sincronitzaran', 'se segueixen' ->
'es segueixen' (correct reflexive pronoun before consonants).
- settings.providers.status: key was 'false' (YAML-coerced), now 'off'
to match settings/en.yml status.off used in view lookups.
- sophtron_items.sophtron_setup_required.message: stripped trailing
blank line from the quoted scalar.
- settings/profiles/show.html.erb: switched 'family_moniker ==
"Group"' branch checks to 'Current.family&.moniker == "Group"'.
After Family#moniker_label started returning translated values,
callers using the display label for branching would render the
household copy for group families in ca. Compare the stored sentinel
instead.
- Did not apply CodeRabbit's webauthn 'eliminada' -> 'desada' suggestion:
the key is wired to the destroy action (verified at
settings/webauthn_credentials_controller.rb:55), so 'eliminada' is
correct.
401 lines
11 KiB
Ruby
401 lines
11 KiB
Ruby
module LanguagesHelper
|
|
LANGUAGE_MAPPING = {
|
|
en: "English",
|
|
ru: "Russian",
|
|
ar: "Arabic",
|
|
bg: "Bulgarian",
|
|
'ca-CAT': "Catalan (Catalonia)",
|
|
ca: "Catalan",
|
|
'da-DK': "Danish (Denmark)",
|
|
'de-AT': "German (Austria)",
|
|
'de-CH': "German (Switzerland)",
|
|
de: "German",
|
|
ee: "Ewe",
|
|
'en-AU': "English (Australia)",
|
|
'en-BORK': "English (Bork)",
|
|
'en-CA': "English (Canada)",
|
|
'en-GB': "English (United Kingdom)",
|
|
'en-IND': "English (India)",
|
|
'en-KE': "English (Kenya)",
|
|
'en-MS': "English (Malaysia)",
|
|
'en-NEP': "English (Nepal)",
|
|
'en-NG': "English (Nigeria)",
|
|
'en-NZ': "English (New Zealand)",
|
|
'en-PAK': "English (Pakistan)",
|
|
'en-SG': "English (Singapore)",
|
|
'en-TH': "English (Thailand)",
|
|
'en-UG': "English (Uganda)",
|
|
'en-US': "English (United States)",
|
|
'en-ZA': "English (South Africa)",
|
|
'en-au-ocker': "English (Australian Ocker)",
|
|
'es-AR': "Spanish (Argentina)",
|
|
'es-MX': "Spanish (Mexico)",
|
|
es: "Spanish",
|
|
fa: "Persian",
|
|
'fi-FI': "Finnish (Finland)",
|
|
fr: "French",
|
|
'fr-CA': "French (Canada)",
|
|
'fr-CH': "French (Switzerland)",
|
|
he: "Hebrew",
|
|
hy: "Armenian",
|
|
id: "Indonesian",
|
|
it: "Italian",
|
|
ja: "Japanese",
|
|
ko: "Korean",
|
|
lt: "Lithuanian",
|
|
lv: "Latvian",
|
|
'mi-NZ': "Maori (New Zealand)",
|
|
'nb-NO': "Norwegian Bokmål (Norway)",
|
|
nl: "Dutch",
|
|
'no-NO': "Norwegian (Norway)",
|
|
pl: "Polish",
|
|
'pt-BR': "Portuguese (Brazil)",
|
|
pt: "Portuguese",
|
|
sk: "Slovak",
|
|
sv: "Swedish",
|
|
th: "Thai",
|
|
tr: "Turkish",
|
|
uk: "Ukrainian",
|
|
vi: "Vietnamese",
|
|
'zh-CN': "简体中文",
|
|
'zh-TW': "繁體中文",
|
|
af: "Afrikaans",
|
|
az: "Azerbaijani",
|
|
be: "Belarusian",
|
|
bn: "Bengali",
|
|
bs: "Bosnian",
|
|
cs: "Czech",
|
|
cy: "Welsh",
|
|
da: "Danish",
|
|
'de-DE': "German (Germany)",
|
|
dz: "Dzongkha",
|
|
'el-CY': "Greek (Cyprus)",
|
|
el: "Greek",
|
|
'en-CY': "English (Cyprus)",
|
|
'en-IE': "English (Ireland)",
|
|
'en-IN': "English (India)",
|
|
'en-TT': "English (Trinidad and Tobago)",
|
|
eo: "Esperanto",
|
|
'es-419': "Spanish (Latin America)",
|
|
'es-CL': "Spanish (Chile)",
|
|
'es-CO': "Spanish (Colombia)",
|
|
'es-CR': "Spanish (Costa Rica)",
|
|
'es-EC': "Spanish (Ecuador)",
|
|
'es-ES': "Spanish (Spain)",
|
|
'es-NI': "Spanish (Nicaragua)",
|
|
'es-PA': "Spanish (Panama)",
|
|
'es-PE': "Spanish (Peru)",
|
|
'es-US': "Spanish (United States)",
|
|
'es-VE': "Spanish (Venezuela)",
|
|
et: "Estonian",
|
|
eu: "Basque",
|
|
fi: "Finnish",
|
|
'fr-FR': "French (France)",
|
|
fy: "Western Frisian",
|
|
gd: "Scottish Gaelic",
|
|
gl: "Galician",
|
|
'hi-IN': "Hindi (India)",
|
|
hi: "Hindi",
|
|
hr: "Croatian",
|
|
hu: "Hungarian",
|
|
is: "Icelandic",
|
|
'it-CH': "Italian (Switzerland)",
|
|
ka: "Georgian",
|
|
kk: "Kazakh",
|
|
km: "Khmer",
|
|
kn: "Kannada",
|
|
lb: "Luxembourgish",
|
|
lo: "Lao",
|
|
mg: "Malagasy",
|
|
mk: "Macedonian",
|
|
ml: "Malayalam",
|
|
mn: "Mongolian",
|
|
'mr-IN': "Marathi (India)",
|
|
ms: "Malay",
|
|
nb: "Norwegian Bokmål",
|
|
ne: "Nepali",
|
|
nn: "Norwegian Nynorsk",
|
|
oc: "Occitan",
|
|
or: "Odia",
|
|
pa: "Punjabi",
|
|
rm: "Romansh",
|
|
ro: "Romanian",
|
|
sc: "Sardinian",
|
|
sl: "Slovenian",
|
|
sq: "Albanian",
|
|
sr: "Serbian",
|
|
st: "Southern Sotho",
|
|
'sv-FI': "Swedish (Finland)",
|
|
'sv-SE': "Swedish (Sweden)",
|
|
sw: "Swahili",
|
|
ta: "Tamil",
|
|
te: "Telugu",
|
|
tl: "Tagalog",
|
|
tt: "Tatar",
|
|
ug: "Uyghur",
|
|
ur: "Urdu",
|
|
uz: "Uzbek",
|
|
wo: "Wolof"
|
|
}.freeze
|
|
|
|
EXCLUDED_LOCALES = [
|
|
# Test locales
|
|
"en-BORK",
|
|
"en-au-ocker",
|
|
# Duplicate locales
|
|
"fr-FR",
|
|
"de-DE",
|
|
"hi-IN",
|
|
"sv-SE",
|
|
"ca-CAT",
|
|
"en-US",
|
|
"fi-FI",
|
|
"en-IND"
|
|
].freeze
|
|
|
|
# Locales with complete/extensive translations
|
|
SUPPORTED_LOCALES = [
|
|
"en", # English
|
|
"fr", # French
|
|
"de", # German
|
|
"es", # Spanish
|
|
"tr", # Turkish
|
|
"nb", # Norwegian Bokmål
|
|
"ca", # Catalan
|
|
"ro", # Romanian
|
|
"pl", # Polish
|
|
"pt-BR", # Brazilian Portuguese
|
|
"zh-CN", # Chinese (Simplified)
|
|
"zh-TW", # Chinese (Traditional)
|
|
"nl", # Dutch
|
|
"hu" # Hungarian
|
|
].freeze
|
|
|
|
COUNTRY_MAPPING = {
|
|
AF: "🇦🇫 Afghanistan",
|
|
AL: "🇦🇱 Albania",
|
|
DZ: "🇩🇿 Algeria",
|
|
AD: "🇦🇩 Andorra",
|
|
AO: "🇦🇴 Angola",
|
|
AG: "🇦🇬 Antigua and Barbuda",
|
|
AR: "🇦🇷 Argentina",
|
|
AM: "🇦🇲 Armenia",
|
|
AU: "🇦🇺 Australia",
|
|
AT: "🇦🇹 Austria",
|
|
AZ: "🇦🇿 Azerbaijan",
|
|
BS: "🇧🇸 Bahamas",
|
|
BH: "🇧🇭 Bahrain",
|
|
BD: "🇧🇩 Bangladesh",
|
|
BB: "🇧🇧 Barbados",
|
|
BY: "🇧🇾 Belarus",
|
|
BE: "🇧🇪 Belgium",
|
|
BZ: "🇧🇿 Belize",
|
|
BJ: "🇧🇯 Benin",
|
|
BT: "🇧🇹 Bhutan",
|
|
BO: "🇧🇴 Bolivia",
|
|
BA: "🇧🇦 Bosnia and Herzegovina",
|
|
BW: "🇧🇼 Botswana",
|
|
BR: "🇧🇷 Brazil",
|
|
BN: "🇧🇳 Brunei",
|
|
BG: "🇧🇬 Bulgaria",
|
|
BF: "🇧🇫 Burkina Faso",
|
|
BI: "🇧🇮 Burundi",
|
|
KH: "🇰🇭 Cambodia",
|
|
CM: "🇨🇲 Cameroon",
|
|
CA: "🇨🇦 Canada",
|
|
CV: "🇨🇻 Cape Verde",
|
|
CF: "🇨🇫 Central African Republic",
|
|
TD: "🇹🇩 Chad",
|
|
CL: "🇨🇱 Chile",
|
|
CN: "🇨🇳 China",
|
|
CO: "🇨🇴 Colombia",
|
|
KM: "🇰🇲 Comoros",
|
|
CG: "🇨🇬 Congo",
|
|
CD: "🇨🇩 Congo, Democratic Republic of the",
|
|
CR: "🇨🇷 Costa Rica",
|
|
CI: "🇨🇮 Côte d'Ivoire",
|
|
HR: "🇭🇷 Croatia",
|
|
CU: "🇨🇺 Cuba",
|
|
CY: "🇨🇾 Cyprus",
|
|
CZ: "🇨🇿 Czech Republic",
|
|
DK: "🇩🇰 Denmark",
|
|
DJ: "🇩🇯 Djibouti",
|
|
DM: "🇩🇲 Dominica",
|
|
DO: "🇩🇴 Dominican Republic",
|
|
EC: "🇪🇨 Ecuador",
|
|
EG: "🇪🇬 Egypt",
|
|
SV: "🇸🇻 El Salvador",
|
|
GQ: "🇬🇶 Equatorial Guinea",
|
|
ER: "🇪🇷 Eritrea",
|
|
EE: "🇪🇪 Estonia",
|
|
ET: "🇪🇹 Ethiopia",
|
|
FJ: "🇫🇯 Fiji",
|
|
FI: "🇫🇮 Finland",
|
|
FR: "🇫🇷 France",
|
|
GA: "🇬🇦 Gabon",
|
|
GM: "🇬🇲 Gambia",
|
|
GE: "🇬🇪 Georgia",
|
|
DE: "🇩🇪 Germany",
|
|
GH: "🇬🇭 Ghana",
|
|
GR: "🇬🇷 Greece",
|
|
GD: "🇬🇩 Grenada",
|
|
GT: "🇬🇹 Guatemala",
|
|
GN: "🇬🇳 Guinea",
|
|
GW: "🇬🇼 Guinea-Bissau",
|
|
GY: "🇬🇾 Guyana",
|
|
HT: "🇭🇹 Haiti",
|
|
HN: "🇭🇳 Honduras",
|
|
HU: "🇭🇺 Hungary",
|
|
IS: "🇮🇸 Iceland",
|
|
IN: "🇮🇳 India",
|
|
ID: "🇮🇩 Indonesia",
|
|
IR: "🇮🇷 Iran",
|
|
IQ: "🇮🇶 Iraq",
|
|
IE: "🇮🇪 Ireland",
|
|
IL: "🇮🇱 Israel",
|
|
IT: "🇮🇹 Italy",
|
|
JM: "🇯🇲 Jamaica",
|
|
JP: "🇯🇵 Japan",
|
|
JO: "🇯🇴 Jordan",
|
|
KZ: "🇰🇿 Kazakhstan",
|
|
KE: "🇰🇪 Kenya",
|
|
KI: "🇰🇮 Kiribati",
|
|
KP: "🇰🇵 North Korea",
|
|
KR: "🇰🇷 South Korea",
|
|
KW: "🇰🇼 Kuwait",
|
|
XK: "🇽🇰 Kosovo",
|
|
KG: "🇰🇬 Kyrgyzstan",
|
|
LA: "🇱🇦 Laos",
|
|
LV: "🇱🇻 Latvia",
|
|
LB: "🇱🇧 Lebanon",
|
|
LS: "🇱🇸 Lesotho",
|
|
LR: "🇱🇷 Liberia",
|
|
LY: "🇱🇾 Libya",
|
|
LI: "🇱🇮 Liechtenstein",
|
|
LT: "🇱🇹 Lithuania",
|
|
LU: "🇱🇺 Luxembourg",
|
|
MK: "🇲🇰 North Macedonia",
|
|
MG: "🇲🇬 Madagascar",
|
|
MW: "🇲🇼 Malawi",
|
|
MY: "🇲🇾 Malaysia",
|
|
MV: "🇲🇻 Maldives",
|
|
ML: "🇲🇱 Mali",
|
|
MT: "🇲🇹 Malta",
|
|
MH: "🇲🇭 Marshall Islands",
|
|
MR: "🇲🇷 Mauritania",
|
|
MU: "🇲🇺 Mauritius",
|
|
MX: "🇲🇽 Mexico",
|
|
FM: "🇫🇲 Micronesia",
|
|
MD: "🇲🇩 Moldova",
|
|
MC: "🇲🇨 Monaco",
|
|
MN: "🇲🇳 Mongolia",
|
|
ME: "🇲🇪 Montenegro",
|
|
MA: "🇲🇦 Morocco",
|
|
MZ: "🇲🇿 Mozambique",
|
|
MM: "🇲🇲 Myanmar",
|
|
NA: "🇳🇦 Namibia",
|
|
NR: "🇳🇷 Nauru",
|
|
NP: "🇳🇵 Nepal",
|
|
NL: "🇳🇱 Netherlands",
|
|
NZ: "🇳🇿 New Zealand",
|
|
NI: "🇳🇮 Nicaragua",
|
|
NE: "🇳🇪 Niger",
|
|
NG: "🇳🇬 Nigeria",
|
|
NO: "🇳🇴 Norway",
|
|
OM: "🇴🇲 Oman",
|
|
PK: "🇵🇰 Pakistan",
|
|
PS: "🇵🇸 Palestine",
|
|
PW: "🇵🇼 Palau",
|
|
PA: "🇵🇦 Panama",
|
|
PG: "🇵🇬 Papua New Guinea",
|
|
PY: "🇵🇾 Paraguay",
|
|
PE: "🇵🇪 Peru",
|
|
PH: "🇵🇭 Philippines",
|
|
PL: "🇵🇱 Poland",
|
|
PT: "🇵🇹 Portugal",
|
|
QA: "🇶🇦 Qatar",
|
|
RO: "🇷🇴 Romania",
|
|
RU: "🇷🇺 Russia",
|
|
RW: "🇷🇼 Rwanda",
|
|
KN: "🇰🇳 Saint Kitts and Nevis",
|
|
LC: "🇱🇨 Saint Lucia",
|
|
VC: "🇻🇨 Saint Vincent and the Grenadines",
|
|
WS: "🇼🇸 Samoa",
|
|
SM: "🇸🇲 San Marino",
|
|
ST: "🇸🇹 Sao Tome and Principe",
|
|
SA: "🇸🇦 Saudi Arabia",
|
|
SN: "🇸🇳 Senegal",
|
|
RS: "🇷🇸 Serbia",
|
|
SC: "🇸🇨 Seychelles",
|
|
SL: "🇸🇱 Sierra Leone",
|
|
SG: "🇸🇬 Singapore",
|
|
SK: "🇸🇰 Slovakia",
|
|
SI: "🇸🇮 Slovenia",
|
|
SB: "🇸🇧 Solomon Islands",
|
|
SO: "🇸🇴 Somalia",
|
|
ZA: "🇿🇦 South Africa",
|
|
SS: "🇸🇸 South Sudan",
|
|
ES: "🇪🇸 Spain",
|
|
LK: "🇱🇰 Sri Lanka",
|
|
SD: "🇸🇩 Sudan",
|
|
SR: "🇸🇷 Suriname",
|
|
SE: "🇸🇪 Sweden",
|
|
CH: "🇨🇭 Switzerland",
|
|
SY: "🇸🇾 Syria",
|
|
TW: "🇹🇼 Taiwan",
|
|
TJ: "🇹🇯 Tajikistan",
|
|
TZ: "🇹🇿 Tanzania",
|
|
TH: "🇹🇭 Thailand",
|
|
TL: "🇹🇱 Timor-Leste",
|
|
TG: "🇹🇬 Togo",
|
|
TO: "🇹🇴 Tonga",
|
|
TT: "🇹🇹 Trinidad and Tobago",
|
|
TN: "🇹🇳 Tunisia",
|
|
TR: "🇹🇷 Turkey",
|
|
TM: "🇹🇲 Turkmenistan",
|
|
TV: "🇹🇻 Tuvalu",
|
|
UG: "🇺🇬 Uganda",
|
|
UA: "🇺🇦 Ukraine",
|
|
AE: "🇦🇪 United Arab Emirates",
|
|
GB: "🇬🇧 United Kingdom",
|
|
US: "🇺🇸 United States",
|
|
UY: "🇺🇾 Uruguay",
|
|
UZ: "🇺🇿 Uzbekistan",
|
|
VU: "🇻🇺 Vanuatu",
|
|
VA: "🇻🇦 Vatican City",
|
|
VE: "🇻🇪 Venezuela",
|
|
VN: "🇻🇳 Vietnam",
|
|
YE: "🇾🇪 Yemen",
|
|
ZM: "🇿🇲 Zambia",
|
|
ZW: "🇿🇼 Zimbabwe"
|
|
}.freeze
|
|
|
|
def country_options
|
|
COUNTRY_MAPPING.keys.map do |key|
|
|
english = COUNTRY_MAPPING[key]
|
|
emoji, name = english.split(" ", 2)
|
|
label = I18n.t("countries.#{key}", default: name)
|
|
[ "#{emoji} #{label}", key ]
|
|
end
|
|
end
|
|
|
|
def language_options
|
|
I18n.available_locales
|
|
.select { |locale| SUPPORTED_LOCALES.include?(locale.to_s) }
|
|
.map do |locale|
|
|
label = LANGUAGE_MAPPING[locale.to_sym] || locale.to_s.humanize
|
|
[ "#{label} (#{locale})", locale ]
|
|
end
|
|
.sort_by { |label, locale| label }
|
|
end
|
|
|
|
def timezone_options
|
|
ActiveSupport::TimeZone.all
|
|
.sort_by { |tz| [ tz.utc_offset, tz.name ] }
|
|
.map do |tz|
|
|
name = tz.name.split(" - ").first.gsub(" (US & Canada)", "")
|
|
[ "(#{tz.formatted_offset}) #{name}", tz.tzinfo.identifier ]
|
|
end
|
|
end
|
|
end
|