mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-31 20:25:14 -04:00
fix: sync locales in user registration validation (#3278)
* Add ability to inject into Python files * Update outdated references to gen_global_components.py * Add code gen for registration locale validation * sort validators * update for pydantic 2 * run generator again --------- Co-authored-by: Gasper Gril <gasper@gril.si> Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
This commit is contained in:
parent
02da2114f9
commit
b54cdf6425
@ -3,8 +3,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, ConfigDict
|
||||||
from utils import PROJECT_DIR, CodeTemplates, HTTPRequest, RouteObject
|
from utils import PROJECT_DIR, CodeTemplates, HTTPRequest, RouteObject, RequestType
|
||||||
|
|
||||||
CWD = Path(__file__).parent
|
CWD = Path(__file__).parent
|
||||||
|
|
||||||
@ -12,23 +12,25 @@ OUTFILE = PROJECT_DIR / "tests" / "utils" / "api_routes" / "__init__.py"
|
|||||||
|
|
||||||
|
|
||||||
class PathObject(BaseModel):
|
class PathObject(BaseModel):
|
||||||
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||||
route_object: RouteObject
|
route_object: RouteObject
|
||||||
http_verbs: list[HTTPRequest]
|
http_verbs: list[HTTPRequest]
|
||||||
|
|
||||||
class Config:
|
|
||||||
arbitrary_types_allowed = True
|
|
||||||
|
|
||||||
|
|
||||||
def get_path_objects(app: FastAPI):
|
def get_path_objects(app: FastAPI):
|
||||||
paths = []
|
paths = []
|
||||||
|
|
||||||
for key, value in app.openapi().items():
|
for key, value in app.openapi().items():
|
||||||
if key == "paths":
|
if key == "paths":
|
||||||
for key, value in value.items():
|
for key, value2 in value.items():
|
||||||
|
verbs = []
|
||||||
|
for k, v in value2.items():
|
||||||
|
verbs.append(HTTPRequest(request_type=k, **v))
|
||||||
|
|
||||||
paths.append(
|
paths.append(
|
||||||
PathObject(
|
PathObject(
|
||||||
route_object=RouteObject(key),
|
route_object=RouteObject(key),
|
||||||
http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()],
|
http_verbs=verbs,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||||||
import dotenv
|
import dotenv
|
||||||
import requests
|
import requests
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from pydantic import Extra
|
from pydantic import ConfigDict
|
||||||
from requests import Response
|
from requests import Response
|
||||||
from utils import CodeDest, CodeKeys, inject_inline, log
|
from utils import CodeDest, CodeKeys, inject_inline, log
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ LOCALE_DATA: dict[str, LocaleData] = {
|
|||||||
"zh-TW": LocaleData(name="繁體中文 (Chinese traditional)"),
|
"zh-TW": LocaleData(name="繁體中文 (Chinese traditional)"),
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCALE_TEMPLATE = """// This Code is auto generated by gen_global_components.py
|
LOCALE_TEMPLATE = """// This Code is auto generated by gen_ts_locales.py
|
||||||
export const LOCALES = [{% for locale in locales %}
|
export const LOCALES = [{% for locale in locales %}
|
||||||
{
|
{
|
||||||
name: "{{ locale.name }}",
|
name: "{{ locale.name }}",
|
||||||
@ -70,6 +70,8 @@ export const LOCALES = [{% for locale in locales %}
|
|||||||
|
|
||||||
|
|
||||||
class TargetLanguage(MealieModel):
|
class TargetLanguage(MealieModel):
|
||||||
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
locale: str
|
locale: str
|
||||||
@ -78,10 +80,6 @@ class TargetLanguage(MealieModel):
|
|||||||
twoLettersCode: str
|
twoLettersCode: str
|
||||||
progress: float = 0.0
|
progress: float = 0.0
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = Extra.allow
|
|
||||||
allow_population_by_field_name = True
|
|
||||||
|
|
||||||
|
|
||||||
class CrowdinApi:
|
class CrowdinApi:
|
||||||
project_name = "Mealie"
|
project_name = "Mealie"
|
||||||
@ -152,6 +150,7 @@ PROJECT_DIR = Path(__file__).parent.parent.parent
|
|||||||
datetime_dir = PROJECT_DIR / "frontend" / "lang" / "dateTimeFormats"
|
datetime_dir = PROJECT_DIR / "frontend" / "lang" / "dateTimeFormats"
|
||||||
locales_dir = PROJECT_DIR / "frontend" / "lang" / "messages"
|
locales_dir = PROJECT_DIR / "frontend" / "lang" / "messages"
|
||||||
nuxt_config = PROJECT_DIR / "frontend" / "nuxt.config.js"
|
nuxt_config = PROJECT_DIR / "frontend" / "nuxt.config.js"
|
||||||
|
reg_valid = PROJECT_DIR / "mealie" / "schema" / "_mealie" / "validators.py"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This snippet walks the message and dat locales directories and generates the import information
|
This snippet walks the message and dat locales directories and generates the import information
|
||||||
@ -175,6 +174,19 @@ def inject_nuxt_values():
|
|||||||
inject_inline(nuxt_config, CodeKeys.nuxt_local_dates, all_date_locales)
|
inject_inline(nuxt_config, CodeKeys.nuxt_local_dates, all_date_locales)
|
||||||
|
|
||||||
|
|
||||||
|
def inject_registration_validation_values():
|
||||||
|
all_langs = []
|
||||||
|
for match in locales_dir.glob("*.json"):
|
||||||
|
lang_string = f'"{match.stem}",'
|
||||||
|
all_langs.append(lang_string)
|
||||||
|
|
||||||
|
# sort
|
||||||
|
all_langs.sort()
|
||||||
|
|
||||||
|
log.debug(f"injecting locales into user registration validation -> {reg_valid}")
|
||||||
|
inject_inline(reg_valid, CodeKeys.nuxt_local_messages, all_langs)
|
||||||
|
|
||||||
|
|
||||||
def generate_locales_ts_file():
|
def generate_locales_ts_file():
|
||||||
api = CrowdinApi("")
|
api = CrowdinApi("")
|
||||||
models = api.get_languages()
|
models = api.get_languages()
|
||||||
@ -193,6 +205,7 @@ def main():
|
|||||||
|
|
||||||
generate_locales_ts_file()
|
generate_locales_ts_file()
|
||||||
inject_nuxt_values()
|
inject_nuxt_values()
|
||||||
|
inject_registration_validation_values()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -6,7 +6,7 @@ from utils import log
|
|||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
template = """// This Code is auto generated by gen_global_components.py
|
template = """// This Code is auto generated by gen_ts_types.py
|
||||||
{% for name in global %}import {{ name }} from "@/components/global/{{ name }}.vue";
|
{% for name in global %}import {{ name }} from "@/components/global/{{ name }}.vue";
|
||||||
{% endfor %}{% for name in layout %}import {{ name }} from "@/components/layout/{{ name }}.vue";
|
{% endfor %}{% for name in layout %}import {{ name }} from "@/components/layout/{{ name }}.vue";
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import re
|
import re
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from humps import camelize
|
from humps import camelize
|
||||||
from pydantic import BaseModel, Extra, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
|
|
||||||
|
|
||||||
@ -34,33 +33,30 @@ class ParameterIn(str, Enum):
|
|||||||
|
|
||||||
|
|
||||||
class RouterParameter(BaseModel):
|
class RouterParameter(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
required: bool = False
|
required: bool = False
|
||||||
name: str
|
name: str
|
||||||
location: ParameterIn = Field(..., alias="in")
|
location: ParameterIn = Field(..., alias="in")
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = Extra.allow
|
|
||||||
|
|
||||||
|
|
||||||
class RequestBody(BaseModel):
|
class RequestBody(BaseModel):
|
||||||
required: bool = False
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
class Config:
|
required: bool = False
|
||||||
extra = Extra.allow
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPRequest(BaseModel):
|
class HTTPRequest(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
request_type: RequestType
|
request_type: RequestType
|
||||||
description: str = ""
|
description: str = ""
|
||||||
summary: str
|
summary: str
|
||||||
requestBody: Optional[RequestBody]
|
request_body: RequestBody | None = None
|
||||||
|
|
||||||
parameters: list[RouterParameter] = []
|
parameters: list[RouterParameter] = []
|
||||||
tags: list[str] | None = []
|
tags: list[str] | None = []
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = Extra.allow
|
|
||||||
|
|
||||||
def list_as_js_object_string(self, parameters, braces=True):
|
def list_as_js_object_string(self, parameters, braces=True):
|
||||||
if len(parameters) == 0:
|
if len(parameters) == 0:
|
||||||
return ""
|
return ""
|
||||||
@ -71,11 +67,11 @@ class HTTPRequest(BaseModel):
|
|||||||
return ", ".join(parameters)
|
return ", ".join(parameters)
|
||||||
|
|
||||||
def payload(self):
|
def payload(self):
|
||||||
return "payload" if self.requestBody else ""
|
return "payload" if self.request_body else ""
|
||||||
|
|
||||||
def function_args(self):
|
def function_args(self):
|
||||||
all_params = [p.name for p in self.parameters]
|
all_params = [p.name for p in self.parameters]
|
||||||
if self.requestBody:
|
if self.request_body:
|
||||||
all_params.append("payload")
|
all_params.append("payload")
|
||||||
return self.list_as_js_object_string(all_params)
|
return self.list_as_js_object_string(all_params)
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class CodeSlicer:
|
|||||||
self._next_line += 1
|
self._next_line += 1
|
||||||
|
|
||||||
|
|
||||||
def get_indentation_of_string(line: str, comment_char: str = "//") -> str:
|
def get_indentation_of_string(line: str, comment_char: str = "//|#") -> str:
|
||||||
return re.sub(rf"{comment_char}.*", "", line).removesuffix("\n")
|
return re.sub(rf"{comment_char}.*", "", line).removesuffix("\n")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// This Code is auto generated by gen_global_components.py
|
// This Code is auto generated by gen_ts_locales.py
|
||||||
export const LOCALES = [
|
export const LOCALES = [
|
||||||
{
|
{
|
||||||
name: "繁體中文 (Chinese traditional)",
|
name: "繁體中文 (Chinese traditional)",
|
||||||
value: "zh-TW",
|
value: "zh-TW",
|
||||||
progress: 30,
|
progress: 29,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -15,7 +15,7 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "Tiếng Việt (Vietnamese)",
|
name: "Tiếng Việt (Vietnamese)",
|
||||||
value: "vi-VN",
|
value: "vi-VN",
|
||||||
progress: 1,
|
progress: 0,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -27,43 +27,43 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "Türkçe (Turkish)",
|
name: "Türkçe (Turkish)",
|
||||||
value: "tr-TR",
|
value: "tr-TR",
|
||||||
progress: 53,
|
progress: 62,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Svenska (Swedish)",
|
name: "Svenska (Swedish)",
|
||||||
value: "sv-SE",
|
value: "sv-SE",
|
||||||
progress: 94,
|
progress: 99,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "српски (Serbian)",
|
name: "српски (Serbian)",
|
||||||
value: "sr-SP",
|
value: "sr-SP",
|
||||||
progress: 32,
|
progress: 31,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Slovenian",
|
name: "Slovenian",
|
||||||
value: "sl-SI",
|
value: "sl-SI",
|
||||||
progress: 47,
|
progress: 49,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Slovak",
|
name: "Slovak",
|
||||||
value: "sk-SK",
|
value: "sk-SK",
|
||||||
progress: 93,
|
progress: 91,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Pусский (Russian)",
|
name: "Pусский (Russian)",
|
||||||
value: "ru-RU",
|
value: "ru-RU",
|
||||||
progress: 98,
|
progress: 99,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Română (Romanian)",
|
name: "Română (Romanian)",
|
||||||
value: "ro-RO",
|
value: "ro-RO",
|
||||||
progress: 42,
|
progress: 44,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -75,19 +75,19 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "Português do Brasil (Brazilian Portuguese)",
|
name: "Português do Brasil (Brazilian Portuguese)",
|
||||||
value: "pt-BR",
|
value: "pt-BR",
|
||||||
progress: 97,
|
progress: 95,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Polski (Polish)",
|
name: "Polski (Polish)",
|
||||||
value: "pl-PL",
|
value: "pl-PL",
|
||||||
progress: 98,
|
progress: 100,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Norsk (Norwegian)",
|
name: "Norsk (Norwegian)",
|
||||||
value: "no-NO",
|
value: "no-NO",
|
||||||
progress: 99,
|
progress: 97,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -99,25 +99,25 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "Latvian",
|
name: "Latvian",
|
||||||
value: "lv-LV",
|
value: "lv-LV",
|
||||||
progress: 1,
|
progress: 0,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Lithuanian",
|
name: "Lithuanian",
|
||||||
value: "lt-LT",
|
value: "lt-LT",
|
||||||
progress: 93,
|
progress: 91,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "한국어 (Korean)",
|
name: "한국어 (Korean)",
|
||||||
value: "ko-KR",
|
value: "ko-KR",
|
||||||
progress: 5,
|
progress: 3,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "日本語 (Japanese)",
|
name: "日本語 (Japanese)",
|
||||||
value: "ja-JP",
|
value: "ja-JP",
|
||||||
progress: 12,
|
progress: 11,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -135,25 +135,25 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "Magyar (Hungarian)",
|
name: "Magyar (Hungarian)",
|
||||||
value: "hu-HU",
|
value: "hu-HU",
|
||||||
progress: 100,
|
progress: 98,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Croatian",
|
name: "Croatian",
|
||||||
value: "hr-HR",
|
value: "hr-HR",
|
||||||
progress: 93,
|
progress: 91,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "עברית (Hebrew)",
|
name: "עברית (Hebrew)",
|
||||||
value: "he-IL",
|
value: "he-IL",
|
||||||
progress: 97,
|
progress: 98,
|
||||||
dir: "rtl",
|
dir: "rtl",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Galician",
|
name: "Galician",
|
||||||
value: "gl-ES",
|
value: "gl-ES",
|
||||||
progress: 1,
|
progress: 3,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -165,19 +165,19 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "French, Canada",
|
name: "French, Canada",
|
||||||
value: "fr-CA",
|
value: "fr-CA",
|
||||||
progress: 97,
|
progress: 95,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Suomi (Finnish)",
|
name: "Suomi (Finnish)",
|
||||||
value: "fi-FI",
|
value: "fi-FI",
|
||||||
progress: 91,
|
progress: 89,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Español (Spanish)",
|
name: "Español (Spanish)",
|
||||||
value: "es-ES",
|
value: "es-ES",
|
||||||
progress: 79,
|
progress: 93,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -189,13 +189,13 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "British English",
|
name: "British English",
|
||||||
value: "en-GB",
|
value: "en-GB",
|
||||||
progress: 3,
|
progress: 2,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ελληνικά (Greek)",
|
name: "Ελληνικά (Greek)",
|
||||||
value: "el-GR",
|
value: "el-GR",
|
||||||
progress: 34,
|
progress: 33,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -219,7 +219,7 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "Català (Catalan)",
|
name: "Català (Catalan)",
|
||||||
value: "ca-ES",
|
value: "ca-ES",
|
||||||
progress: 75,
|
progress: 74,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -231,13 +231,13 @@ export const LOCALES = [
|
|||||||
{
|
{
|
||||||
name: "العربية (Arabic)",
|
name: "العربية (Arabic)",
|
||||||
value: "ar-SA",
|
value: "ar-SA",
|
||||||
progress: 20,
|
progress: 18,
|
||||||
dir: "rtl",
|
dir: "rtl",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Afrikaans (Afrikaans)",
|
name: "Afrikaans (Afrikaans)",
|
||||||
value: "af-ZA",
|
value: "af-ZA",
|
||||||
progress: 92,
|
progress: 90,
|
||||||
dir: "ltr",
|
dir: "ltr",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
2
frontend/types/components.d.ts
vendored
2
frontend/types/components.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
// This Code is auto generated by gen_global_components.py
|
// This Code is auto generated by gen_ts_types.py
|
||||||
import AdvancedOnly from "@/components/global/AdvancedOnly.vue";
|
import AdvancedOnly from "@/components/global/AdvancedOnly.vue";
|
||||||
import AppButtonCopy from "@/components/global/AppButtonCopy.vue";
|
import AppButtonCopy from "@/components/global/AppButtonCopy.vue";
|
||||||
import AppButtonUpload from "@/components/global/AppButtonUpload.vue";
|
import AppButtonUpload from "@/components/global/AppButtonUpload.vue";
|
||||||
|
@ -1,38 +1,47 @@
|
|||||||
def validate_locale(locale: str) -> bool:
|
def validate_locale(locale: str) -> bool:
|
||||||
valid = {
|
valid = {
|
||||||
"el-GR",
|
# CODE_GEN_ID: MESSAGE_LOCALES
|
||||||
"it-IT",
|
|
||||||
"ko-KR",
|
|
||||||
"es-ES",
|
|
||||||
"ja-JP",
|
|
||||||
"zh-CN",
|
|
||||||
"tr-TR",
|
|
||||||
"ar-SA",
|
|
||||||
"hu-HU",
|
|
||||||
"pt-PT",
|
|
||||||
"no-NO",
|
|
||||||
"sv-SE",
|
|
||||||
"ro-RO",
|
|
||||||
"sk-SK",
|
|
||||||
"uk-UA",
|
|
||||||
"fr-CA",
|
|
||||||
"pl-PL",
|
|
||||||
"da-DK",
|
|
||||||
"pt-BR",
|
|
||||||
"de-DE",
|
|
||||||
"ca-ES",
|
|
||||||
"sr-SP",
|
|
||||||
"cs-CZ",
|
|
||||||
"fr-FR",
|
|
||||||
"zh-TW",
|
|
||||||
"af-ZA",
|
"af-ZA",
|
||||||
"ru-RU",
|
"ar-SA",
|
||||||
"he-IL",
|
"bg-BG",
|
||||||
"nl-NL",
|
"ca-ES",
|
||||||
"en-US",
|
"cs-CZ",
|
||||||
|
"da-DK",
|
||||||
|
"de-DE",
|
||||||
|
"el-GR",
|
||||||
"en-GB",
|
"en-GB",
|
||||||
|
"en-US",
|
||||||
|
"es-ES",
|
||||||
"fi-FI",
|
"fi-FI",
|
||||||
|
"fr-CA",
|
||||||
|
"fr-FR",
|
||||||
|
"gl-ES",
|
||||||
|
"he-IL",
|
||||||
|
"hr-HR",
|
||||||
|
"hu-HU",
|
||||||
|
"is-IS",
|
||||||
|
"it-IT",
|
||||||
|
"ja-JP",
|
||||||
|
"ko-KR",
|
||||||
|
"lt-LT",
|
||||||
|
"lv-LV",
|
||||||
|
"nl-NL",
|
||||||
|
"no-NO",
|
||||||
|
"pl-PL",
|
||||||
|
"pt-BR",
|
||||||
|
"pt-PT",
|
||||||
|
"ro-RO",
|
||||||
|
"ru-RU",
|
||||||
|
"sk-SK",
|
||||||
|
"sl-SI",
|
||||||
|
"sr-SP",
|
||||||
|
"sv-SE",
|
||||||
|
"tr-TR",
|
||||||
|
"uk-UA",
|
||||||
"vi-VN",
|
"vi-VN",
|
||||||
|
"zh-CN",
|
||||||
|
"zh-TW",
|
||||||
|
# END: MESSAGE_LOCALES
|
||||||
}
|
}
|
||||||
|
|
||||||
return locale in valid
|
return locale in valid
|
||||||
|
Loading…
x
Reference in New Issue
Block a user